如何使用CRTP使用基类创建可选模板参数?

时间:2018-04-12 09:55:42

标签: c++ templates crtp

如何使用以下代码中的CRTP使用基类创建可选模板参数?

template <unsigned int BYTES, OPTIONAL typename DerivedPrinter = MonoPrinter>  //DerivedPrinter should be optional. If it is not specified then it should default to MonoPrinter.
class MonoPrinter
{
protected:
    unsigned char CtrlCodes[BYTES] = { 0xFF };   //A code to initialize the printer

public:
    MonoPrinter()
    {
    }

    DerivedPrinter& print(const char* d)
    {
        for (int i=0; i<sizeof(CtrlCodes); i++)
          SendCtrlCode(CtrlCodes[i]);     //Initialize the printer and send additional control codes for color, font, etc...

        printf("Print Me: %s\n", d);  //This would actually send the string of chars to the printer (not to stdout) for printing
        return static_cast<DerivedPrinter&>(*this);     //Return a reference to the Derived Printer a la CRTP
    }
};

template <unsigned int BYTES>
class ColorPrinter : public MonoPrinter<BYTES, ColorPrinter>
{
public:
    ColorPrinter() : MonoPrinter()
    {
        static_assert(sizeof(CtrlCodes) >= 4);
        CtrlCodes[1] = 0xAA;
        CtrlCodes[2] = 0xBB;
        CtrlCodes[3] = 0xC0;
    }

    ColorPrinter& SetColor(unsigned char c)
    {
        CtrlCodes[3] = c;
        return *this;
    }
};


void main(void)
{
    MonoPrinter<1> iMonoPrinter;
    ColorPrinter<4> iColorPrinter;

    iMonoPrinter.print("Hello World").print(" we have no color");
    iColorPrinter.print("Hello World").SetColor(BLUE).print(" in Living Color");
}

P.S。
上面的代码是为了简单而设计和删节。
&#34; BYTES&#34; template参数不是可选的,必须始终指定 我有这个代码的其他问题,但主要的是如何使&#34; DerivedPrinter&#34;模板参数是可选的,因此并不总是必须指定...而当它不是 - 它应该默认为基类本身。

2 个答案:

答案 0 :(得分:2)

我想你可以(见下面的代码),但我认为在这种情况下没有必要(见第二个例子)。

第一个示例,使用可选的模板参数(请注意,此处PrinterTpl模板直接从具体的BasePrinter继承,因此所有派生类MonoPrinterColorPrinter都在此处,继承自BasePrinter):

template <unsigned int BYTES>
class BasePrinter
{
protected:
    unsigned char CtrlCodes[BYTES] = { 0xFF };

public:
    BasePrinter()
    {
        SendCtrlCode(CtrlCodes[0]);  //Initialize the printer
    }
};

template <unsigned int BYTES, typename DerivedPrinter = BasePrinter<BYTES>>  //DerivedPrinter should be optional. If it is not specified then it should default to PrinterTpl.
class PrinterTpl : public BasePrinter<BYTES>
{
public:
    PrinterTpl() : BasePrinter<BYTES>()
    {
    }

    DerivedPrinter& print(const char* d)
    {
        printf("Data: %s\n", d);
        return static_cast<DerivedPrinter&>(*this);     //Return a reference to the Derived Printer a la CRTP
    }
};

template <unsigned int BYTES>
class MonoPrinter : public PrinterTpl<BYTES, MonoPrinter<BYTES>>
{
public:
    MonoPrinter() : PrinterTpl<BYTES, MonoPrinter<BYTES>>()
    {
    }
};

template <unsigned int BYTES>
class ColorPrinter : public PrinterTpl<BYTES, ColorPrinter<BYTES>>
{
public:
    ColorPrinter() : PrinterTpl<BYTES, ColorPrinter<BYTES>>()
    {
        static_assert(sizeof(this->CtrlCodes) >= 4, "CtrlCodes too small");
        this->CtrlCodes[1] = 0xC1;
        this->CtrlCodes[2] = 0xC2;
        this->CtrlCodes[3] = 0xC3;
    }

    ColorPrinter& SetColor(int c)
    {
        assert(c < sizeof(this->CtrlCodes));
        SendCtrlCode(this->CtrlCodes[c+1]);
        return *this;
    }
};

第二个例子,没有模板可选参数(这里模板PrinterTpl不需要从基础继承):

template <unsigned int BYTES, typename ConcretePrinter>
class PrinterTpl
{
protected:
    unsigned char CtrlCodes[BYTES] = { 0xFF };

public:
    PrinterTpl()
    {
        SendCtrlCode(this->CtrlCodes[0]);  //Initialize the printer
    }

    ConcretePrinter& print(const char* d)
    {
        printf("Data: %s\n", d);
        return static_cast<ConcretePrinter&>(*this);     //Return a reference to the Derived Printer a la CRTP
    }
};

template <unsigned int BYTES>
class MonoPrinter : public PrinterTpl<BYTES, MonoPrinter<BYTES>>
{
public:
    MonoPrinter() : PrinterTpl<BYTES, MonoPrinter<BYTES>>()
    {
    }
};

template <unsigned int BYTES>
class ColorPrinter : public PrinterTpl<BYTES, ColorPrinter<BYTES>>
{
public:
    ColorPrinter() : PrinterTpl<BYTES, ColorPrinter<BYTES>>()
    {
        static_assert(sizeof(this->CtrlCodes) >= 4, "CtrlCodes too small");
        this->CtrlCodes[1] = 0xC1;
        this->CtrlCodes[2] = 0xC2;
        this->CtrlCodes[3] = 0xC3;
    }

    ColorPrinter& SetColor(int c)
    {
        assert(c < sizeof(this->CtrlCodes));
        SendCtrlCode(this->CtrlCodes[c+1]);
        return *this;
    }
};

如果我没有弄错的话,这应该达到你的目标,而且我认为它更清洁。

答案 1 :(得分:1)

能够编写MonoPrinter<1>而不是MonoPrinter<1,dummy>并使第二个模板参数可选的关键是基类模板中的以下条件typedef typename

typedef typename std::conditional< std::is_same<Derived, void >::value, MonoPrinter, Derived >::type DerivedPrinter;  //Default to the MonoPrinter class if Derived == void

下面的代码现在编译时没有错误,也不需要创建第3类模板。请参阅:https://godbolt.org/g/awuck7

#include <type_traits>
#include <stdio.h>

#define BLUE 3

template <unsigned int BYTES, typename Derived = void>
class MonoPrinter
{
    typedef typename std::conditional< std::is_same<Derived, void >::value, MonoPrinter, Derived >::type DerivedPrinter;   //Default to the MonoPrinter class if Derived == void

protected:
    unsigned char CtrlCodes[BYTES];
    const unsigned char FinCode = 0xFF;

public:
    void SendCtrlCode(unsigned char c)
    {
        printf("<%02X>", c);    //This would actually send the string of control chars to the printer (not to stdout)
    }

    void InitializePrinter(void)
    {
        printf("\n");
        SendCtrlCode(CtrlCodes[0]);
        SendCtrlCode(0x00);
        SendCtrlCode(FinCode);
    }

    MonoPrinter()
    {
        CtrlCodes[0] = 0xEE;  //Set the default printer escape code
        InitializePrinter();
    }

    MonoPrinter(unsigned char c)
    {
        CtrlCodes[0] = c;  //A custom printer escape code
        InitializePrinter();
    }

    DerivedPrinter& print(const char* d)
    {
        for (int i = 0; i < sizeof(CtrlCodes); i++)
            SendCtrlCode(CtrlCodes[i]);     //Initialize the printer and send additional control codes for color, font, etc...
        SendCtrlCode(FinCode);

        printf("%s", d);  //This would actually send the string of chars to the printer (not to stdout) for printing
        return static_cast<DerivedPrinter&>(*this);     //Return a reference to the Derived Printer a la CRTP
    }

    int FooFn()
    {
        return 333;
    }
};


template <unsigned int BYTES>
class ColorPrinter : public MonoPrinter<BYTES, ColorPrinter<BYTES>>
{
protected:
    using MonoPrinter<BYTES, ColorPrinter<BYTES>>::CtrlCodes;
    //using MonoPrinter<BYTES, ColorPrinter<BYTES>>::FinCode;

public:
    //using MonoPrinter<BYTES, ColorPrinter<BYTES>>::MonoPrinter;
    using MonoPrinter<BYTES, ColorPrinter<BYTES>>::FooFn;
    //using MonoPrinter<BYTES, ColorPrinter<BYTES>>::InitializePrinter;
    //using MonoPrinter<BYTES, ColorPrinter<BYTES>>::SendCtrlCode;

    ColorPrinter()
    {
        static_assert(sizeof(this->CtrlCodes) >= 4, "CtrlCodes too small");
        CtrlCodes[1] = 0xDD;
        CtrlCodes[2] = 0xEE;
        CtrlCodes[3] = 0xC0;  //Default Color value
    }

    ColorPrinter(unsigned char c) : MonoPrinter<BYTES, ColorPrinter<BYTES>>::MonoPrinter(c)
    {
        static_assert(sizeof(this->CtrlCodes) >= 4, "CtrlCodes too small");
        CtrlCodes[1] = 0xDD;
        CtrlCodes[2] = 0xEE;
        CtrlCodes[3] = 0xC0;  //Default Color value
    }

    ColorPrinter& SetColor(unsigned char c)
    {
        CtrlCodes[3] = c;
        return *this;
    }

    int BooFn()
    {
        return FooFn() + 1;
    }
};


int main(void)
{
    MonoPrinter<1> iMonoPrinter;
    ColorPrinter<4> iColorPrinter(0xCC);

    iMonoPrinter.print("Hello World").print(" we have no color \n");
    iColorPrinter.print("Hello World").SetColor(BLUE).print(" in Living Color \n");

    printf(" %d\n", iColorPrinter.FooFn());
    printf(" %d\n", iColorPrinter.BooFn());

    return 0;
}