TLTR:我想根据存储在可变参数模板列表中的索引定义的特定顺序将模板容器中的一些数组映射到函数的参数(我想不出更简单的方法来定义问题)。
数组是使用存储的,void*
但数组和函数参数之间的类型安全由辅助类保证。同一个帮助类必须扩展给定的参数包,获取适当的数组,将它们绑定到函数指针并调用函数。这就是我卡住的地方。
详细信息:对于冗长的问题和无法编译的发布代码,我提前道歉,但我尽量简洁。
问题在于将容器的正确成员映射到函子对象。容器有一个由typelist定义的数组列表,其实现类似于这个。
为简单起见,我们假设 typelist 辅助对象TLAlg::length<TL>
和TLAlg::TypeAt
被定义并允许用户分别访问 typelist 的长度和第 N 个类型。
容器类为类型列表(称为字段)中的每种类型分配一个数组,并存储一个指向这些缓冲区的不透明指针。实现类型安全的 getter 以访问特定的字段索引。其实现如下:
// container class, stores an array for each type in the typelist
template<class TL>
class Volume {
public:
// list of opaque buffers
void *opaque_buffers[TLAlg::length<TL>::value];
template<int size>
Volume(const size_t (&dim)[size]){
// each opaque_buffers is initialized here
...
}
// getters are using the field index for type-safety
template <int index> typename
TLAlg::TypeAt<TL, index>::Result &
get(const std::initializer_list<size_t> &position);
};
我们想要实现一个Functor
对象,该对象将使用类型列表的特定子集在卷上应用给定函数。用户不是直接操作数组,而是给出他想要访问的字段的索引列表和要应用的函数的指针。函子对象负责设置正确的参数。
为了提高类型安全性,我们将它们分成两个列表:只读和读/写(读const
和非const
)。给定的函数原型必须与函子对象的定义一致:代码只有在给定的函数指针与参数定义完全匹配时才能编译,所以我们不必担心类型不匹配。函子的实现是:
template<typename TL, class T1, class T2> struct Functor{};
template< typename TL,
template<size_t...> class T1, size_t... A1, // read only arguments
template<size_t...> class T2, size_t... A2 // read-write arguments
>
struct Functor< TL, T1<A1...>, T2<A2...> >{
// type of the function pointer
typedef void (*Type)(const typename TLAlg::TypeAt<TL, A1>::Result* ... ,
typename TLAlg::TypeAt<TL, A2>::Result* ...);
Functor(Volume<TL> &v, Type f): f(f){
// At this point we have everything we need: the volume, the function
// and the list of arguments, but how to combine them all?
// maybe some magic here to map the opaque pointers to the arguments?
}
void operator()(){
// or maybe here?
}
}
如您所见,函子目前没有做任何事情,因为我不知道如何将两个参数包映射到容器数组并将它们绑定到函数指针......
为清楚起见,下面是仿函数类的使用示例:
// main Typelist
typedef Typelist<float, Typelist<double, Typelist<int, NullType>>> Pixel;
// function we want to apply: reads first and last field of the list and updates second
void foo(const float *f1,
const int *f3,
double *f2){}
// helper class to store the parameter packs
template<size_t ... T> struct Pack {};
int main(){
// volume declaration
Volume<Pixel> volume((size[]){1024,1024});
// delare typesafe functor
Functor<Pixel, // typelist
Pack<0,2>, // list of read-only fields
Pack<1> // list of read-write fields
> apply_foo(volume, foo);
apply_foo(); // <- this does nothing at the moment
}
我尝试了std::forward
很std::bind
长时间,但我还没有找到正确的解决方案。可能会考虑替换类型列表,std::tuple
但最好保留当前定义。
这段代码可能看起来很奇怪而且不必要地复杂,但它是一个非常简化的大型框架版本,使用这些类是有意义的。
任何帮助将不胜感激。
Yakk的回答澄清:
我确实需要一个类型列表,因为我在其中做了更多的魔法,例如列表的每个元素都可以是一个元组而不是单个类型来关联一个名称。这允许整洁的代码,如:
typedef MakeTypeList((float, p),
(float, bnd),
(float, gosa)) HimenoFields;
// I can now use aliases, and the declaration order does not matter in the code.
// here is an access to the second field:
volume.get<HimenoFields::bnd>({0,0,0});
你可以想象这如何与我想用函子实现的那种操作很好地结合起来。
其次,我明白你为什么对吸气剂感到困惑。正如我最初所说,这是一个非常简化的代码版本,尽管问题很长。在实际程序中,体积是多维的,要么在单个数组中展平,要么在多维数组中分配,这就是 getter 需要完整坐标的原因。这些 getter 有几种不同参数的实现。
最后,Functor 不需要知道将函数应用到哪个元素,因为它本身控制迭代空间并应用预定义的骨架(即模板、波前......)。为了简单起见,我再次省略了它。