TLTR:我想根据存储在变量模板列表中的索引定义的特定顺序将一些数组从模板容器映射到函数的参数(我想不出一种更简单的方法来定义这个问题)。
数组使用void*存储,但是数组和函数参数之间的类型安全由助手类来保证。同一个助手类必须展开给定的参数包,获取适当的数组,将它们绑定到函数指针并调用函数。这就是我被困的地方。
detail:我对冗长的问题和发布的代码表示歉意,这段代码没有编译,但我尽力做到尽可能简洁。
问题在于将容器的适当成员映射到函子对象。容器有一个由打字员定义的数组列表,其实现类似于这一个。
为了简单起见,我们假设定义了打字员助手对象TLAlg::length<TL>和TLAlg::TypeAt,并允许用户分别访问打字员和Nth类型的长度。
容器类为打字员(称为字段)中的每个类型分配一个数组,并存储指向这些缓冲区的不透明指针。实现类型a 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对象,它将使用打字员的特定子集在卷上应用给定的函数。用户提供要访问的字段的索引列表和要应用的函数的指针,而不是直接操作数组。函子对象负责设置正确的参数。
为了提高类型安全性,我们将它们分开为两个列表:只读和读/写(read 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有几种不同参数的实现。
最后,函子不需要知道要将函数应用到哪个元素,因为它本身控制迭代空间并应用预定义的骨架(即模板、波前.)。再一次,为了简单起见,我忽略了这一点。
发布于 2013-04-02 04:26:36
首先,我要重写你的type_list
template<typename... Ts>
struct type_list {};而不是你的18次论据攻击。因此,编写type_at<n>::type和index_of<T>::value并不难。这些和基于对的TypeList之间的映射也不困难。
template<typename list>
struct make_TypeList;
template<typename T0, typename T1, typename...Ts>
struct make_TypeList<type_list<T0, T1, Ts...>> {
typedef typename make_Typelist< type_list<T1, Ts...> >::type tail;
typedef TypeList< T0, tail > type;
};
template<typename T0>
struct make_TypeList<type_list<T0>> {
typedef TypeList< T0, NullType > type;
};
template<>
struct make_TypeList<type_list<>> {
typedef NullType type;
};如果你真的需要的话。有理由使用非类型列表,但您没有演示任何内容。
构建编译时类型索引集合有点棘手,但如果传入上限,则可以这样做。目标是创建一个序列:
template<size_t... s>
struct seq {};如果您在编译时获得这些索引,这就更容易了。一旦您有了这个序列,并且有了type_at,您就可以编写一个类似于这样的调用函数:
template<size_t... s, typename... Ts>
void evaluate( seq<s...> unused, void(*func)(Ts*... ts) ) {
func( &get_at<s>()... );
}我们直接将序列解压缩到函数调用中。实际上,所讨论的序列通常只是0,1,2,3,4,...,n-1,它可以很容易地生成:
template<size_t max, size_t... s>
struct make_seq:make_seq<max-1, max-1, s...> {};
template<size_t... s>
struct make_seq<0, s...> {
typedef seq<s...> type;
};要明确的是:operator()在执行make_seq<sizeof...(Ts)>::type()之后调用助手函数,将其传递给助手函数,然后助手函数调用func( &get_at<s>(/*n maybe?*/)... ),bob是您的叔叔。
有一件事让我感到困惑:
// 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);我不知道为什么需要const std::initializer_list<size_t> &position,或者至少为什么没有:
template <int index> typename
TLAlg::TypeAt<TL, index>::Result &
get_at(size_t n);这使我认为,如果您的operator()是多个类型的数组,那么函数器中的Volume就会缺少“应用此函子的哪个索引”。
但是,我强烈怀疑“创建一个索引包,调用一个助手函数,这样您就可以得到内核,然后在函数调用中展开这个包”是您所缺少的窍门。
https://stackoverflow.com/questions/15748483
复制相似问题