简体   繁体   English

如何实例化编译时/静态多态的类型列表

[英]How to instantiate a list of types for compile-time/static polymorphism

I'm implementing a compile time dispatcher which makes use of static polymorphism and metaprogramming.我正在实现一个使用 static 多态性和元编程的编译时调度程序。

I have a list of types which I would like to instantiate into a runtime std::array .我有一个类型列表,我想将其实例化为运行时std::array

struct Test
{
     typedef std::integral_constant<int,0> nop;
     typedef std::integral_constant<int,1> A;
     typedef std::integral_constant<int,2> B;
     typedef std::integral_constant<int,3> C;

     using list = mp_list<A, B, C>; // mp_list expands to: template<A, B, C> struct {};

     struct Things{
         int (Test::*process)(int foo, float bar);
         const std::string key;
         int something;
         float other;
     };

     typedef std::array<Things, mp_size<list>::value> Thing_list;
     Thing_list thing_list;

     template<typename T=nop> int process(int foo, float bar);

     // stuff...

     Test();
}

In the above code, mp_list is simply a variadic template which 'expands' to struct<A, B, C> mp_list .在上面的代码中, mp_list只是一个“扩展”为struct<A, B, C> mp_list的可变参数模板。 And likewise, mp_size gives an mp implementation of the sizeof .同样, mp_size给出了sizeof的 mp 实现。

As can be inferred, the Thing_list is an array with a compile-time known size.可以推断, Thing_list是一个具有编译时已知大小的数组。

I can then specialize a template function like so:然后我可以像这样专门化一个模板 function :

template<> int process<Test::B>(int foo, float bar){ /* do stuff */ };

to achieve compile-time polymorphism.实现编译时多态性。

The above code works well, except that to initialize it, I am stuck at doing this in the constructor:上面的代码运行良好,除了初始化它,我坚持在构造函数中这样做:

Test::Test() thing_list({{{&Test::process<A>, "A"}, // this all should be achieved through meta-programming
                          {&Test::process<B>, "B"},
                          {&Test::process<C>, "C"}}}} )
{
   // stuff
}

There are two things I'm unable to get right:有两件事我无法做对:

  1. I would like to have a MP based initialization of the list.我想要一个基于 MP 的列表初始化。 As I update the list definition in the declaration, I would like my initialization to automatically reflect that list type.当我更新声明中的list定义时,我希望我的初始化能够自动反映该列表类型。
  2. I would also like to avoid having to duplicate the name of the type as a string literal.我还想避免将类型的名称复制为字符串文字。 I have tried to use something like integral_constant but the use of const char* as a template parameter seems to be forbidden.我曾尝试使用类似integral_constant的东西,但似乎禁止使用const char*作为模板参数。 I am left to having to duplicate declaration (triplicate, really).我不得不重复声明(真的是一式三份)。

This answer is almost the solution: it takes a list of types and instantiates them like so:这个答案几乎是解决方案:它需要一个类型列表并像这样实例化它们:

static std::tuple<int*, float*, foo*, bar*> CreateList() {
return { Create<int>(), Create<float>(), Create<foo>(), Create<bar>() };
}

However, I'm stuck on converting from std::tuple to std::array .但是,我坚持从std::tuple转换为std::array

The main question is #1.主要问题是#1。 Bonus for #2 without using #define based trickery.不使用基于#define的诡计的 #2 奖励。

If anyone cares: this code is destined for embedded software.如果有人在乎:这段代码是为嵌入式软件设计的。 There are dozens of different types, and importantly, each type (eg A , B , C ) will have an identically structured configuration to be loaded from memory (eg under a configuration key for "A" ) - hence the reason of wanting to have access to the string name of the type at runtime.有几十种不同的类型,重要的是,每种类型(例如ABC )都将具有从 memory 加载的相同结构的配置(例如,在"A"的配置键下) - 因此想要拥有在运行时访问类型的字符串名称。

Not sure to understand what do you exactly want but...不知道你到底想要什么,但......

Given that you can use at least C++17 (for auto template parameters), you can define outside your class some variables as鉴于您至少可以使用 C++17 (用于auto模板参数),您可以在 class 之外定义一些变量为

static constexpr char nops[] = "NOP";
static constexpr char A[] = "A";
static constexpr char B[] = "B";
static constexpr char C[] = "C";

Then a simple wrapper that accept nops , A , B , etc. as template parameters然后是一个简单的包装器,它接受nopsAB等作为模板参数

template <auto val>
struct wrap
 { };

Then a using that, given a variadic list of template value parameters, create a mp_list of wrap types然后using它,给定模板值参数的可变参数列表,创建wrap类型的mp_list

template <auto ... vals>
using wrapper = mp_list<wrap<vals>...>;

At this point... I suppose that, inside Test , you can define nop and list as follows此时......我想,在里面Test ,你可以定义noplist如下

using nop = wrap<nops>;

using list = wrapper<A, B, C>;

Using delegating constructor, meta-programming way to initialize your thing_list could be the following使用委托构造函数,初始化你的thing_list的元编程方式可能如下

template <auto ... vals>
Test (mp_list<wrap<vals>...>) 
   : thing_list{{{&Test::process<wrap<vals>>, vals}...}}
 { }

Test () : Test{list{}}
 { }

If you modify the list adding a D parameter (where D is the "D" literal)如果您修改list添加D参数(其中D"D"字面量)

using list = wrapper<A, B, C, D>;

automagically the you get an additional {&Test::process<wrap<D>>, D} element in your thing_list .你会自动在你的thing_list中获得一个额外的{&Test::process<wrap<D>>, D}元素。

The following is a full compiling C++17 example下面是一个完整的编译C++17的例子

#include <array>
#include <string>
#include <type_traits>

template <typename...>
struct mp_list
 { };

template <typename>
struct mp_size;

template <typename ... Ts>
struct mp_size<mp_list<Ts...>>
   : public std::integral_constant<std::size_t, sizeof...(Ts)>
 { };

static constexpr char nops[] = "NOP";
static constexpr char A[] = "A";
static constexpr char B[] = "B";
static constexpr char C[] = "C";

template <auto val>
struct wrap
 { };

template <auto ... vals>
using wrapper = mp_list<wrap<vals>...>;


struct Test
 {
   using nop = wrap<nops>;

   using list = wrapper<A, B, C>;

   struct Things
    {
      int (Test::*process)(int foo, float bar);
      const std::string key;
      //   int something;
      //   float other;
    };

   using Thing_list = std::array<Things, mp_size<list>::value>;

   Thing_list thing_list;

   template<typename T=nop> int process(int foo, float bar)
    { return 0; }

   template <auto ... vals>
   Test (mp_list<wrap<vals>...>) 
      : thing_list{{{&Test::process<wrap<vals>>, vals}...}}
    { }

   Test () : Test{list{}}
    { }
 };

int main ()
 {
   Test  t;
 }

I would suggest changing the typedef s for A, B and C to struct so you can define the string inside them.我建议将 A、B 和 C 的typedef更改为 struct,以便您可以在其中定义字符串。

struct A {
    static constexpr int value = 1;
    static constexpr char name[] = "A";
};

// Same for B and C

using list = mp_list<A, B, C>;

Then you can create a make_thing_list然后你可以创建一个make_thing_list

template <typename... T>
static std::array<Things, sizeof...(T)> make_thing_list(mp_list<T...>) {
    return {{{&Test::process<T>, T::name}...}};
}

auto thing_list = make_thing_list(list{});

Complete example完整示例

#include <string>
#include <array>
#include <iostream>

template <typename... T>
struct mp_list {};

struct Test
{
    struct nop {
        static constexpr int value = 0;
        static constexpr char name[] = "nop";
    };
    struct A {
        static constexpr int value = 1;
        static constexpr char name[] = "A";
    };
    struct B {
        static constexpr int value = 2;
        static constexpr char name[] = "B";
    };
    struct C {
        static constexpr int value = 3;
        static constexpr char name[] = "C";
    };

     using list = mp_list<A, B, C>; // mp_list expands to: template<A, B, C> struct {};

     struct Things{
         int (Test::*process)(int foo, float bar);
         const std::string key;
         int something;
         float other;
     };

    template <typename... T>
    static std::array<Things, sizeof...(T)> make_thing_list(mp_list<T...>) {
        return {{{&Test::process<T>, T::name}...}};
    }

    using Thing_list = decltype(make_thing_list(list{}));

    Thing_list thing_list = make_thing_list(list{});

     template<typename T=nop> int process(int foo, float bar) {
         return T::value;
     }

     // stuff...

     Test() {}
};

int main() {
    Test t;

    static_assert(std::is_same_v<decltype(t.thing_list), std::array<Test::Things, 3>>);

    for (auto& thing : t.thing_list) {
        std::cout << thing.key << (t.*thing.process)(1, 1.0) << '\n';
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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