понедельник, 27 августа 2018 г.

Curiously Recurring Template Pattern (CRTP)

https://ru.wikipedia.org/wiki/Странно_рекурсивный_шаблон

#include <iostream>  
 
using namespace std;  
 
template<typename T>  
class BaseSinglton  
{  
    protected:  
        BaseSinglton() {}  
        virtual ~BaseSinglton() {}
    public:  
        static T& instance()   
        {
            static T oObj;   
            return oObj;   
        }
};  
 
class Settings : public BaseSinglton<Settings>  
{  
    friend class BaseSinglton<Settings>;  
    private:  
        Settings() : BaseSinglton<Settings>() {}  
        virtual ~Settings() {}  
};
 
int main()   
{  
    Settings &oSetting = Settings::instance();  
    printf("poSetting = %p", &oSetting);  
    return 0;  
}  

воскресенье, 26 августа 2018 г.

Псевдозамыкания на С

 #include <stdio.h>  
 #include <stdlib.h>  
 #include <stdarg.h>  

static int _dump(char *psDstBuf_      , size_t nSizeDstBuf_, 
                 const void *pSrcBuf_ , size_t nSizeSrcBuf_)  
{  
    const char aHexSymbol[] = { 
                                '0', '1', '2', '3', '4', '5', '6', '7',
                                '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 
                              };  
    size_t iSrcBuf = 0;  
    size_t iDstBuf = 0;  
    unsigned char cByte = '\0';

    if ( nSizeDstBuf_ < 3 ) { return 0; }
  
    for (iSrcBuf=0; iSrcBuf<nSizeSrcBuf_; ++iSrcBuf) {
      if ( ( nSizeDestBuf_ - iSrcBuf * 3 ) < 3 ) { break; }
      cByte = ((const unsigned char*) pSrcBuf_)[iSrcBuf];
      psDstBuf_[iDstBuf++] = aHexSymbol[(cByte >> 4) & 0b1111];
      psDstBuf_[iDstBuf++] = aHexSymbol[cByte & 0b1111];
      psDstBuf_[iDstBuf++] = ( (iSrcBuf & 0b1111) == 0b1111) ? '\n' : ' ';
    }

    psDstBuf_[iDstBuf ? iDstBuf - 1 : 0] = '\0';

    return ( iSrcBuf * 3 );  
}

#define LOG(fBufFiller, nSizeData)                        \
({                                                        \  
    char *psDumpMessage = (char*) calloc(nSizeData, 1);   \  
                                                          \
    fBufFiller(psDumpMessage);                            \
                                                          \
    printf("%s", psDumpMessage);                          \
                                                          \
    free(psDumpMessage);                                  \
})
  
static void _message_error(const char *psFormat_, ...)  
{  
    va_list nArgs;

    va_start(nArgs, psFormat_);  
    size_t nSizeMsg = vsnprintf(NULL, 0, psFormat_, nArgs);  
    va_end(nArgs);  
    
    #define fBufFiller(psBuf)                \
    ({                                       \
        va_start(nArgs, psFormat_);          \
        vsprintf(psBuf, psFormat_, nArgs);   \
        va_end(nArgs);                       \
    })
  
    LOG(fBufFiller, nSizeMsg);
  
    #undef fBufFiller  
}

static void _message_dump(void *pDataDump_, unsigned int nSizeDump_)  
{  
    const size_t nSizeBuf = nSizeDump_ * 3;  
    
    #define fBufFiller(psBuf)                             \
    ({                                                    \
        _dump(psBuf, nSizeBuf, pDataDump_, nSizeDump_);   \  
    })

    LOG(fBufFiller, nSizeBuf);

    #undef fBufFiller  
}

static void _message_state_report(int iModule_, int nError_)  
{  
    const size_t nSizeBuf = 64;
 
    #define fBufFiller(psBuf)                                              \
    ({                                                                     \
        const char *psState = nError_ ? "unworkable" : "workable";         \
        sprintf(psBuf, "Report for module #%d: %s ", iModule_, psState);   \  
    })

    LOG(fBufFiller, nSizeBuf);

    #undef fBufFiller  
}

int main(void)  
{  
    int aRandomData[] = { 9, 8, 7, 6, 5 };

    _message_error("Sending packet '%s' is failed (code error: %d)", "bpdu_tcn", 3);  
    printf("\n\n");  

    _message_state_report(3, 5);  
    printf("\n\n");  

    _message_dump(aRandomData, sizeof(aRandomData));  
    printf("\n\n");  
    
    return 0;
}

воскресенье, 5 августа 2018 г.

Именованные параметры. K-Macros

https://blog.tartanllama.xyz/simple-named-bools/

При вызове функции типа

 cake make_cake (bool with_dairy, bool chocolate_sauce, bool poison)  

явна видно проблема ненаглядности передаваемых параметров и, как следствие, отсутствие понимания намерений програмиста.

 auto c = make_cake(true, true, false);  

Что означает первый true, второй true и третий false ? Совершенно непонятно и надо заглядывать в определение функции.

Для разрешения данной проблемы можно использовать следующий подход

 const bool with_dairy      = true;  
 const bool chocolate_sauce = true;  
 const bool poison          = false;  
 auto c = make_cake(with_dairy, chocolate_sauce, poison);  

Недостаток очевиден - введены три переменные, абсолютно ненужные для правильного исполнения кода, исключительно для улучшения читабельности. Дополнительно происходит 'раздутие' кодовой базы - вместо 1 строчки получается 4 строчки.

Можно попробовать и так

 auto c = make_cake(  
                     true  /* with_dairy      */,   
                     true  /* chocolate_sauce */,   
                     false /* poison          */  
                   );   

Лишних переменных нет, но также происходит 'раздутие' кодовой базы.

Данное затруднение является частным случаем того, что в языках С/С++ нет нативной поддержки так называемых именованных параметров. Много способов разрешить данную проблемы содержится в статье - https://habr.com/company/infopulse/blog/246663/. Также имеется мнение, что, по крайней мере в С++, именованные параметры в общем случае и не нужны - https://habr.com/post/246711/.

Simon Brand (blog.tartanllama.xyz) предлагает свое решение

 #define K(name) true  
   
 auto c = make_cake(K(with_dairy), !K(chocolate_sauce), !K(poison));  

Преимуществом является относительная наглядность, отсутствие 'раздутия' кодовой базы.
Недостатком является некоторая необычность синтаксиса.

воскресенье, 29 июля 2018 г.

Обработка перечислений (enum)

  Код с применением switch

 /* handler.h */    
   
 #ifndef HANDLER_H  
 #define HANDLER_H  
   
 typedef enum {    
   RED,    
   GREEN,    
   BLUE,    
   MAX_COLOR;  
 } COLOR;   
   
 bool handleRedColor(unsigned int nIntensity_);  
 bool handleGreenColor(unsigned int nIntensity_);  
 bool handleBlueColor(unsigned int nIntensity_);  
 bool handleColor(COLOR oTypeColor_, unsigned int nIntensity_);  
   
 #endif /* HANDLER_H */  
 /* handler.с */  
   
 #include <handler.h>  
   
 bool handleRedColor(unsigned int nIntensity_  ) { /* ... */ return true; }  
 bool handleGreenColor(unsigned int nIntensity_) { /* ... */ return true; }  
 bool handleBlueColor(unsigned int nIntensity_ ) { /* ... */ return true; }  
   
 bool handleColor(COLOR oTypeColor_, unsigned int nIntensity_)  
 {  
   switch ( oTypeColor_ ) {  
    case RED   : return handleRedColor(nIntensity_);  
    case GREEN : return handleGreenColor(nIntensity_);  
    case BLUE  : return handleBlueColor(nIntensity_);  
   }  
   return false;  
 }  
Преимущества:
  • в функции  handleColor(..) не нужно проверять входной параметр oTypeColor на валидность, так как вызов функции-обработчика будет производится только для валидных значений, а для всех невалидных будет возвращаться false
  • при изменении enum COLOR будет выводиться предупреждение компилятора о том, что в switch не обрабатывается соответствцющий элемент (только gcc ?)
Недостатки:
  • при изменении enum нужно править код, обрабатываемый switch (код вызова обработчиков)

Код без применением switch

 /* handler.h */    
   
 #ifndef HANDLER_H  
 #define HANDLER_H  
   
 typedef enum {    
   RED,    
   GREEN,    
   BLUE,    
   MAX_COLOR;  
 } COLOR;   
   
 bool handleRedColor(unsigned int nIntensity_);  
 bool handleGreenColor(unsigned int nIntensity_);  
 bool handleBlueColor(unsigned int nIntensity_);  
 bool handleColor(COLOR oTypeColor_, unsigned int nIntensity_);  
   
 #endif /* HANDLER_H */  
 /* handler.c */  

 #include <handler.h>
   
 bool handleRedColor(unsigned int nIntensity_  ) { /* ... */ return true; }  
 bool handleGreenColor(unsigned int nIntensity_) { /* ... */ return true; }  
 bool handleBlueColor(unsigned int nIntensity_ ) { /* ... */ return true; }  
   
 typedef bool(HANDLER_COLOR) (unsigned int nIntensity_);  
   
 static HANDLER_COLOR aHandlerList[MAX_COLOR] =  
 {  
    handleRedColor,  
    handleGreenColor,  
    handleBlueColor   
 };  
   
   
 bool handleColor(COLOR oTypeColor_, unsigned int nIntensity_)  
 {  
    if ( oTypeColor_ < 0            ) { return false; }  
    if ( oTypeColor_ >= __MAX_COLOR ) { return false; }  
    return aHandlerList[oTypeColor_](nIntensity_);  
 }  
Преимущества:
  • при изменении enum не нужно править код вызова обработчиков
Недостатки:
  • функции  handleColor(..) нужно проверять входной параметр oTypeColor_на валидность
  • вручную отслеживать соответствие наличия и порядка следования массива обработчиков  enum-у COLOR

Код без применением switch с использованием X-Macros


https://stackoverflow.com/questions/6635851/real-world-use-of-x-macros
https://en.wikipedia.org/wiki/X_Macro
 /* handler.h */    
     
 #ifndef HANDLER_H   
 #define HANDLER_H   
    
 #define HANDLER_TABLE               \  
     ENTRY(RED  , handleRedColor  )  \  
     ENTRY(GREEN, handleGreenColor)  \  
     ENTRY(BLUE , handleBlueColor )   
    
 typedef enum {  
  #define ENTRY(OBJ_TYPE, OBJ_HANDLER) OBJ_TYPE,  
   HANDLER_TABLE  
  #undef ENTRY  
   MAX_COLOR;   
 } COLOR;   
   
 bool handleRedColor(unsigned int nIntensity_);  
 bool handleGreenColor(unsigned int nIntensity_);  
 bool handleBlueColor(unsigned int nIntensity_);  
   
 bool handleColor(COLOR oTypeColor_, unsigned int nIntensity_);  
   
 #endif /* HANDLER_H */  
 /* handler.c */  
   
 #include <handler.h>

 bool handleRedColor(unsigned int nIntensity_  ) { /* ... */ return true; }  
 bool handleGreenColor(unsigned int nIntensity_) { /* ... */ return true; }  
 bool handleBlueColor(unsigned int nIntensity_ ) { /* ... */ return true; }  
   
 typedef bool(HANDLER_COLOR) (unsigned int nIntensity_);  
   
 static HANDLER_COLOR aHandlerList[MAX_COLOR] =  
 {  
   #define ENTRY(OBJ_TYPE, OBJ_HANDLER) OBJ_HANDLER,  
     HANDLER_TABLE  
   #undef ENTRY  
 };  
   
 bool handleColor(COLOR oTypeColor_, unsigned int nIntensity_)  
 {  
    if ( oTypeColor_ < 0          ) { return false; }  
    if ( oTypeColor_ >= MAX_COLOR ) { return false; }  
    return aHandlerList[oTypeColor_](nIntensity_);  
 }  
Преимущества:
  • при изменении enum не нужно править код вызова обработчиков
  • очень просто отслеживать пару элемент - обработчик
Недостатки:
  • в функции  handleColor(..) нужно проверять входной параметр oTypeColor на валидность

воскресенье, 22 июля 2018 г.

Неопределённый результат: i = i++

http://alenacpp.blogspot.com/2005/11/sequence-points.html
https://habr.com/post/216189/
https://ru.stackoverflow.com/questions/590020/О-порядке-вычисления-выражений

Согласно стандарту C++ порядок вычисления аргументов функции не специфицирован, что означает, что компиляторы могут выбрать любой порядок вычисления аргументов.
Из стандарта C++ (1.9 Program execution)
"3 Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified
(for example, order of evaluation of arguments to a function)."

§5/4 Стандарт говорит:
"Между двумя точками следования скалярный объект должен менять хранимое значение при вычислении выражения не более одного раза."

ТОЧКИ СЛЕДОВАНИЯ НЕАКТУАЛЬНЫ ДЛЯ СОВРЕМЕННОГО СТАНДАРТА С++ !!!