【问题标题】:C++: Boost.MPL equivalent for calling a function on the i-th type in a type vectorC++:Boost.MPL 等效于调用类型向量中第 i 个类型的函数
【发布时间】:2014-08-04 07:32:26
【问题描述】:

我设计了一个这样的函数:

template<template<class> class TMapper, class TResult = void, class TUserContext>
static typename TResult mapParam(int index, TUserContext ctx)

它需要一个TMapper,它需要符合以下概念:

template<class T> struct Mapper { static TResult map(int index, TUserContent ctx) {} }

Mapper 类型为每个参数实例化一次,但“map”函数仅针对给定索引处的参数调用。 Mapper 使用参数的类型 (T) 进行参数化。在测试中,我们希望能够对我们甚至不知道存在的参数做一些事情,并且仍然可以使用它的精确类型,但不需要将任何模板元编程添加到真实的测试用例中......

有没有办法在 Boost.MPL 中做到这一点,以便阅读此代码的人不需要理解它,而可以只使用 boost 知识,或者至少在这里有更详细的文档?

这是完整的代码(T::TParams 是任意函数参数类型的 boost::mpl::vector):

template<template<class> class TMapper, class TResult = void, class TUserContext>
        static typename TResult mapParam(int index, TUserContext ctx) {
            return mapParamInternal<TMapper, TResult, boost::mpl::size<T::TParams>::value - 1>(index, ctx);
        }

template<template<class> class TMapper, class TResult, int CurrentIndex, class TUserContext>
static typename boost::enable_if_c<CurrentIndex >= 0, TResult>::type mapParamInternal(int targetIndex, TUserContext ctx) {
    if(CurrentIndex == targetIndex) {
        return TMapper<
            typename boost::mpl::at<
                T::TParams, 
                boost::mpl::int_<CurrentIndex>
            >::type>::map(targetIndex, ctx);
    } else {
        return mapParamInternal<TMapper, TResult, CurrentIndex - 1>(targetIndex, ctx);
    }
}

template<template<class> class TMapper, class TResult, int CurrentIndex, class TUserContext>
static typename boost::disable_if_c<CurrentIndex >= 0, TResult>::type mapParamInternal(int targetIndex, TUserContext ctx) {
    UNREFERENCED_PARAMETER(targetIndex);
    UNREFERENCED_PARAMETER(ctx);
    return TResult();
}

举个例子说明这可能是如何有用的:

template<class TParam>
struct UpdateParameter {
    static void map(int index, CallSerializerTest<T>* self) {
        self->trace.updateParameter(
            TCallSig::getParamName(index), 
            TypeUpdatedValue<TParam>::get());
    }
};

void updateParameter(int index) {
    updatedParams.push_back(index);
    TCallSig::mapParam<UpdateParameter>(index, this);
}

对于“index”给出的参数,上面的代码将只调用一次“self->trace.updateParameter”,而“TParam”将具有该参数的正确类型。 “TCallSig”是定义了“mapParam”函数的任意调用签名。我们通过 Google 测试类型参数化测试获得了大量这些签名。我认为这是一个非常酷的系统,一旦你掌握了元编程的窍门,它就会使测试用例非常简洁易读。

【问题讨论】:

  • 我已将问题阅读了两遍,但我仍然不清楚您在尝试什么。但至少我可以给你一些注意事项:1) 我认为你正在混合运行时/编译时值,这是不可能的。特别是在您的映射函数中,您有一个将传递的(运行时)索引与当前(编译时)索引进行比较的条件。 C++ 没有 static-if 特性,所以两个分支都会被编译。您确定两个分支的返回类型相同吗?恐怕不行。
  • 另外我不明白的原因是因为缺乏更多信息(示例中的TCallSig是什么?请尝试重新表述问题以提供有关该问题的更多信息。谢谢
  • @Manu343726:除了已经写好的,我不知道还能说什么。您是否也阅读了底部的示例? TCallSig 与算法无关。它只是定义 mapParam() 的上下文。是的,我在这里混合编译/运行时,它也不是一个尝试,它有效;)。我只是想知道 boost 是否有合适的 MPL 算法。我只是查看了 fold、accumulate、for_each 等……它们似乎都缺乏所需的功能。
  • 我实际上还不确定它是否在所有情况下都有效。返回值不再是绝对必要的,因此您也可以忽略它们。我认为如果我尝试更多花哨的签名,它们可能会在路上造成一些麻烦。一般的想法是只为类型列表中的第 i 个类型评估给定的 Mapper。我认为特别是标题非常准确地总结了它。这里解决的主要问题是“i-th”是运行时,而类型是编译时。上述算法设法混合了两个世界。

标签: c++ templates boost template-meta-programming boost-mpl


【解决方案1】:

检查Boost.Fusion 库。该库旨在充当编译时和运行时世界之间的桥梁,提供使用这两个世界的属性的特性和算法。
该库具有函数式非可变精神,并提供类似于函数式语言的算法,例如 map、fold 等。但关键是 Fusion 能够在此类构造中使用编译时属性(感谢 MPL) .例如(摘自简介):

template <typename Sequence>
void xml_print_pointers(Sequence const& seq)
{
    for_each(filter_if<boost::is_pointer<_> >(seq), print_xml());
}

如您所见,仅当 runtime 序列的每个元素是指针(这是元素类型的编译时属性)时,它才会对每个元素执行操作。

我相信您可以使用这些结构轻松实现您的算法。

【讨论】:

  • 我从未研究过 Boost.Fusion。我认为它可以使用 Boost.Fusion 的“iter_fold”。它为您提供了一个状态,它可能只是对返回值的引用,并允许您在每个折叠操作中访问当前类型迭代器,这允许您将索引与 targetIndex 进行比较,并且仅在该索引相等时才执行某些操作。我还没有尝试过,但我认为这可能会解决它。
猜你喜欢
  • 1970-01-01
  • 2023-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-05
  • 1970-01-01
相关资源
最近更新 更多