动态编译时混入

发布于 2024-10-25 18:48:22 字数 4265 浏览 1 评论 0 原文

我试图想出一种在编译时定义动态 mixins 的方法。我目前有一个非常老套的解决方案,只能部分实现我想要的功能,但我不知道如何改进它。

我知道更多类似 C++ 的解决方案使用类型列表,但它们要求所有类型都静态定义,这是我试图避免的。

这主要只是为了更好地学习 C++ 的思想练习,我确信我当前的实现不是很好的 C++。欢迎任何改进建议或尝试不同的想法。

我当前实现的主要问题是:

  • 每个 mixin 类只知道其自身以及层次结构中低于其自身的类。我希望每个 mixin 类都能够返回具有不同底层类型的新 mixin。在下面的示例代码中,我希望 PrintOnce 能够有一个返回 PrintTwice 的方法。 > > 对象。
  • 这个想法的任何使用都需要有一个标头包含的顺序。一开始的所有样板代码都需要位于一个标头中,然后需要定义所有 mixin 类,最后可以定义 make_mixed 函数。目前,在 make_mixed 函数之后定义的任何 mixin 都会被忽略。
  • 宏的实现和一般的hackyness。

对于篇幅,我深表歉意,但这是我能想到的最简单的例子。

感谢您的任何帮助。

#include <string>
#include <vector>
#include <iostream>

using namespace std;

template<class Underlying>
struct Printer
{
    Printer(const Underlying &val) : val_(val) {}

    Underlying get_val() { return val_; }

  private:
    Underlying val_;
};

#define CURRENT_NUMBER_MIXED_IN_CLASSES() \
    MixinCount<0, __LINE__>::value

#define INCREMENT_MIXIN_CLASS_COUNTER() \
    template<int id>                                               \
    struct MixinClassCounter< CURRENT_NUMBER_MIXED_IN_CLASSES(), id>    \
    {                                                                   \
        static const bool is_defined = true;                            \
    }

template< bool b, typename i, typename j >
struct select_value;

template<class i, class j>
struct select_value<true, i, j>
{
    static const int value = i::value;
};

template<class i, class j>
struct select_value<false, i, j>
{
    static const int value = j::value;
};

template<int i>
struct IntToVal
{
    static const int value = i;
};

namespace
{
    template<int count, int id>
    struct MixinClassCounter
    {
        static const bool is_defined = false;
    };

    template<int count, int id>
    struct MixinCount
    {
        static const int value = select_value<MixinClassCounter<count, id>::is_defined,
                                              MixinCount<count + 1, id>,
                                              IntToVal<count> >::value;
    };

    template<class Underlying, int i>
    struct MixinBuilder {};

    template<class Underlying>
    struct MixinBuilder<Underlying, 0>
    {
        typedef Printer<Underlying> type;
    };
    INCREMENT_MIXIN_CLASS_COUNTER();
}

#define DECLARE_MIXIN_BEGIN(name) \
    template<class Base> \
    struct name : Base   \
    {                    \
        template<class Underlying>                  \
        name(const Underlying &val) : Base(val) {}

#define DECLARE_MIXIN_END(name)                 \
    };                                          \
    namespace                                   \
    {                                               \
        template<class Underlying>                                         \
        struct MixinBuilder<Underlying, CURRENT_NUMBER_MIXED_IN_CLASSES()> \
        {                                                                   \
            typedef name< typename MixinBuilder<Underlying, CURRENT_NUMBER_MIXED_IN_CLASSES() - 1>::type > type; \
        };                                                                  \
        INCREMENT_MIXIN_CLASS_COUNTER();                                    \
    }                                                                       \


DECLARE_MIXIN_BEGIN(PrintOnce)
void print_once()
{
    cout << Base::get_val() << endl;
}
DECLARE_MIXIN_END(PrintOnce)

DECLARE_MIXIN_BEGIN(PrintTwice)
void print_twice()
{
    cout << Base::get_val() << endl;
    cout << Base::get_val() << endl;
}
DECLARE_MIXIN_END(PrintTwice)

template<class T>
typename MixinBuilder<T, CURRENT_NUMBER_MIXED_IN_CLASSES() - 1>::type make_mixed(const T &val)
{
    return typename MixinBuilder<T, CURRENT_NUMBER_MIXED_IN_CLASSES() - 1>::type(val);
}

int main()
{
    string test("this is a test");
    auto printable_string = make_mixed(test);

    printable_string.print_once();
    printable_string.print_twice();
}

I'm trying to come up with a way to define dynamic mixins at compile time. I currently have a very hacky solution that only partially does what I want, but I'm not sure how to improve it.

I'm aware of some more C++ like solutions using typelist's, but they require all of the types to be defined statically which I'm trying to avoid.

This is mostly just a thought exercise to learn C++ better, and I'm sure that my current implementation is not very good C++. Any suggestions for improvements or different ideas to try would be welcome.

The main problems with my current implementation are:

  • Each mixin class is only aware of itself and classes lower then itself in the hierarchy. I'd like each mixin class to be able to return a new mixin with a different underlying type. In the example code below I'd like PrintOnce to be able to have a method which returns a PrintTwice<PrintOnce<Printer<int > > > object.
  • Any usage of this idea would need to have an order of header inclusion. All the boiler plate code in the beginning would need to be in one header, then all of the mixin classes would need to be defined, and finally the make_mixed function could be defined. Currently any mixin defined after the make_mixed function is ignored.
  • The macro's and general hackyness of the implementation.

I apologize for the length, but this is the most simplified example I could come up with.

Thanks for any help.

#include <string>
#include <vector>
#include <iostream>

using namespace std;

template<class Underlying>
struct Printer
{
    Printer(const Underlying &val) : val_(val) {}

    Underlying get_val() { return val_; }

  private:
    Underlying val_;
};

#define CURRENT_NUMBER_MIXED_IN_CLASSES() \
    MixinCount<0, __LINE__>::value

#define INCREMENT_MIXIN_CLASS_COUNTER() \
    template<int id>                                               \
    struct MixinClassCounter< CURRENT_NUMBER_MIXED_IN_CLASSES(), id>    \
    {                                                                   \
        static const bool is_defined = true;                            \
    }

template< bool b, typename i, typename j >
struct select_value;

template<class i, class j>
struct select_value<true, i, j>
{
    static const int value = i::value;
};

template<class i, class j>
struct select_value<false, i, j>
{
    static const int value = j::value;
};

template<int i>
struct IntToVal
{
    static const int value = i;
};

namespace
{
    template<int count, int id>
    struct MixinClassCounter
    {
        static const bool is_defined = false;
    };

    template<int count, int id>
    struct MixinCount
    {
        static const int value = select_value<MixinClassCounter<count, id>::is_defined,
                                              MixinCount<count + 1, id>,
                                              IntToVal<count> >::value;
    };

    template<class Underlying, int i>
    struct MixinBuilder {};

    template<class Underlying>
    struct MixinBuilder<Underlying, 0>
    {
        typedef Printer<Underlying> type;
    };
    INCREMENT_MIXIN_CLASS_COUNTER();
}

#define DECLARE_MIXIN_BEGIN(name) \
    template<class Base> \
    struct name : Base   \
    {                    \
        template<class Underlying>                  \
        name(const Underlying &val) : Base(val) {}

#define DECLARE_MIXIN_END(name)                 \
    };                                          \
    namespace                                   \
    {                                               \
        template<class Underlying>                                         \
        struct MixinBuilder<Underlying, CURRENT_NUMBER_MIXED_IN_CLASSES()> \
        {                                                                   \
            typedef name< typename MixinBuilder<Underlying, CURRENT_NUMBER_MIXED_IN_CLASSES() - 1>::type > type; \
        };                                                                  \
        INCREMENT_MIXIN_CLASS_COUNTER();                                    \
    }                                                                       \


DECLARE_MIXIN_BEGIN(PrintOnce)
void print_once()
{
    cout << Base::get_val() << endl;
}
DECLARE_MIXIN_END(PrintOnce)

DECLARE_MIXIN_BEGIN(PrintTwice)
void print_twice()
{
    cout << Base::get_val() << endl;
    cout << Base::get_val() << endl;
}
DECLARE_MIXIN_END(PrintTwice)

template<class T>
typename MixinBuilder<T, CURRENT_NUMBER_MIXED_IN_CLASSES() - 1>::type make_mixed(const T &val)
{
    return typename MixinBuilder<T, CURRENT_NUMBER_MIXED_IN_CLASSES() - 1>::type(val);
}

int main()
{
    string test("this is a test");
    auto printable_string = make_mixed(test);

    printable_string.print_once();
    printable_string.print_twice();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

纵情客 2024-11-01 18:48:22

这是一个没有宏的更干净的解决方案:

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

template <typename T>
struct RefWrapper
{
    T *self;
    RefWrapper () : self (nullptr) {abort ();} // should never be called
    RefWrapper (T &self) : self (&self) {}
};


template <typename T>
struct PrintOnce : virtual RefWrapper<T>
{
    PrintOnce () {}             // workaround gcc 4.6 bug
    void print_once () {cout << *RefWrapper<T>::self << endl;}
};


template <typename T>
struct PrintTwice : virtual RefWrapper<T>
{
    PrintTwice () {}            // workaround gcc 4.6 bug
    void print_twice ()
    {
        cout << *RefWrapper<T>::self << endl;
        cout << *RefWrapper<T>::self << endl;
    }
};


template <typename T, typename... Args>
struct Mixed : Args...
{
    Mixed (T &self) :
        RefWrapper<T> (self),
        Args ()... {}

    Mixed (const Mixed ©) :
        RefWrapper<T> (*copy.self),
        Args ()... {}
};


template <template <typename U> class Mixin, typename T>
Mixed<T, Mixin<T> > add_mixin (T &original)
{
    return Mixed<T, Mixin<T> > (original);
}


template <template <typename U> class Mixin, typename T,
          typename... OtherMixins>
Mixed<T, OtherMixins..., Mixin<T> >
add_mixin (Mixed<T, OtherMixins...> &original)
{
    return Mixed<T, OtherMixins..., Mixin<T> > (*original.self);
}


int main ()
{
    string foo = "test";

    auto p1 = add_mixin<PrintOnce> (foo);
    p1.print_once ();

    auto p2 = add_mixin<PrintTwice> (p1);
    p2.print_once ();
    p2.print_twice ();
}

不幸的是,它仍然无法满足您让每个 mixin 类了解所有其他 mixin 类的要求。不过,我不确定这在编译时是否可能。

Here's a cleaner solution without macros:

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

template <typename T>
struct RefWrapper
{
    T *self;
    RefWrapper () : self (nullptr) {abort ();} // should never be called
    RefWrapper (T &self) : self (&self) {}
};


template <typename T>
struct PrintOnce : virtual RefWrapper<T>
{
    PrintOnce () {}             // workaround gcc 4.6 bug
    void print_once () {cout << *RefWrapper<T>::self << endl;}
};


template <typename T>
struct PrintTwice : virtual RefWrapper<T>
{
    PrintTwice () {}            // workaround gcc 4.6 bug
    void print_twice ()
    {
        cout << *RefWrapper<T>::self << endl;
        cout << *RefWrapper<T>::self << endl;
    }
};


template <typename T, typename... Args>
struct Mixed : Args...
{
    Mixed (T &self) :
        RefWrapper<T> (self),
        Args ()... {}

    Mixed (const Mixed ©) :
        RefWrapper<T> (*copy.self),
        Args ()... {}
};


template <template <typename U> class Mixin, typename T>
Mixed<T, Mixin<T> > add_mixin (T &original)
{
    return Mixed<T, Mixin<T> > (original);
}


template <template <typename U> class Mixin, typename T,
          typename... OtherMixins>
Mixed<T, OtherMixins..., Mixin<T> >
add_mixin (Mixed<T, OtherMixins...> &original)
{
    return Mixed<T, OtherMixins..., Mixin<T> > (*original.self);
}


int main ()
{
    string foo = "test";

    auto p1 = add_mixin<PrintOnce> (foo);
    p1.print_once ();

    auto p2 = add_mixin<PrintTwice> (p1);
    p2.print_once ();
    p2.print_twice ();
}

Unfortunately, it still doesn't accomplish your requirement of having each mixin class being aware of all other mixin classes. I'm not sure if that's even possible at compile-time, though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文