jeudi 15 novembre 2018

Meta Mixin.. is that even a thing?

I want to present a "pattern of mixin based structure"(is this even a term?) but not quite sure if it would hold up in "some situation". Basic idea is to generate "type using template class" that multiply inherit mixins. So the type declaration would look like: typedef BaseType<Mixin1, Mixin2, MixinN> Type1; Some accomplishments by the approach:

  • Type1's special feature like operator overloads and Constructor overloads are always available.
  • Explicit type casting overhead is abstracted away by BaseType.
  • C++ multiple implicit conversion barrier is not a problem.

Usual template mixin approach form here looks like: template<class Base> class Printing : public Base {...}. Main drawback for me with this approach:

  • It is necessary to explicitly cast Printing to Base to use some of Base's special features, Or have to provide those overloads explicitly (I know it would just be a matter of one line of codes). But in some situation it would be irritating.

That is why I have come up with the idea to generate the base. Please take a look at the implementation ("some situation"):

#include <iostream>
#include <functional>

#ifdef QT_CORE_LIB
#include <QString>
#endif


template<template<class> class... mixin_t>
class StringType : public mixin_t<StringType<mixin_t>>...
{
    std::string _value;

public:
    StringType() : _value("") {}

    StringType(const StringType &other) = default; // Copy

    StringType(StringType &&other) = default; // Move

#ifdef QT_CORE_LIB
    StringType(const QString &value) { this->_value = value.toStdString(); }
#endif

    StringType(const std::string &value) { _value = value; }

    StringType(const char *value) { _value = value; }

    template<template<class> class T>
    StringType(const StringType<T> &value)
    {
        _value = static_cast<const std::string &>(value);
    }


    StringType &operator=(const StringType &rhs) = default; // copy assign
    StringType &operator=(StringType &&rhs) = default; // Move assign


#ifdef QT_CORE_LIB
    operator QString() const { return QString::fromStdString(_value);}
#endif

    operator std::string() const { return _value; }

    operator const char *() const{ return _value.c_str(); }

    template<template<class> class T>
    operator StringType<T>() const { return _value; }
};




template<class this_t> struct _empty_mixn {};

template<class this_t> struct ToStringMixin
{
    this_t toString() const { return *static_cast<const this_t *>(this); }
};

template<class this_t> struct StringPrinterMixin
{
    void print() const
    {
        std::cout << "From the printer: " << *static_cast<const this_t *>(this);
    }
};




typedef StringType<_empty_mixn> String;
typedef StringType<ToStringMixin> Message;
typedef StringType<ToStringMixin, StringPrinterMixin> PrinterAttachedString;




int main()
{
    Message msg1(String("msg1\n"));
    std::cout << msg1;
    std::cout << "toString() : " << msg1.toString();

    Message msg2 = String("msg2\n");
    std::cout << msg2;
    std::cout << "toString() : " << msg2.toString();

    Message msg3(std::string("msg3\n"));
    std::cout << msg3;
    std::cout << "toString() : " << msg3.toString();

    Message msg4 = std::string("msg4\n");
    std::cout << msg4;
    std::cout << "toString() : " << msg4.toString();

    Message msg5("msg5\n");
    std::cout << msg5;
    std::cout << "toString() : " << msg5.toString();

    Message msg6 = "msg6\n";
    std::cout << msg6;
    std::cout << "toString() : " << msg6.toString();

    std::cout << "\n---------------------\n\n";

    PrinterAttachedString str1(String("str1\n"));
    std::cout << str1;
    std::cout << "toString() : " << str1.toString();
    str1.print();

    PrinterAttachedString str2 = String("str2\n");
    std::cout << str2;
    std::cout << "toString() : " << str2.toString();
    str2.print();

    PrinterAttachedString str3(std::string("str3\n"));
    std::cout << str3;
    std::cout << "toString() : " << str3.toString();
    str3.print();

    PrinterAttachedString str4 = std::string("str4\n");
    std::cout << str4;
    std::cout << "toString() : " << str4.toString();
    str4.print();

    PrinterAttachedString str5("str5\n");
    std::cout << str5;
    std::cout << "toString() : " << str5.toString();
    str5.print();

    PrinterAttachedString str6 = "str6\n";
    std::cout << str6;
    std::cout << "toString() : " << str6.toString();
    str6.print();

    return 0;
}

So, my questions:

  • Would it be practical use this in a situation where operator overloading/implicit casting feature necessary?
  • Does it seem, there would be a necessity of virtual inheritance?
  • Are there any other implementation like this (My search was a failure)?
  • Finally, is there a thing called "meta mixin" that would provide a type's special features?

Please ask if more clarification is needed. Thanks a lot.

Aucun commentaire:

Enregistrer un commentaire