简体   繁体   中英

How to make an optional template parameter with a base class using CRTP?

How to make an optional template parameter with the base class using CRTP in the following code ?

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");
}

PS
The above code is a contrived and abridged for simplicity.
The "BYTES" template parameter is not optional and it always must be specified.
I have other problems with this code, but the main one is how to make the "DerivedPrinter" template parameter optional, so it does not always have to be specified ...and when it is not - it should default to the base class itself.

I guess you can (see code below), but I think it's not necessary in this case (see second example).

First example, with the optional template parameter (note that here the PrinterTpl template inherits directly from the concrete BasePrinter , so all derived classes, MonoPrinter and ColorPrinter here, are inheriting from 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;
    }
};

Second example, no template optional parameter (here the template PrinterTpl doesn't need to inherit from a base):

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;
    }
};

If I am not mistaken, this should achieve your goal and it's cleaner in my opinion.

The key to being able to write MonoPrinter<1> instead of MonoPrinter<1,dummy> and making the 2nd template parameter optional was the following conditional typedef typename inside the base class template:

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

The code below now compiles without errors and does not require the creation of a 3rd class template. See: 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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM