根据布尔标准转换每个参数包的值

Transform each of parameter pack's values based on a boolean criteria

我试图在 C++ TMP 中解决这个问题,我需要将一个参数包类型转换为另一个参数包类型,然后再将类型和值转换回。转换后部分基于一个布尔标准,即 arg in Args... 是否首先被转换。

基本上,我有一包(Args...)。首先,我转换它 (for each args[i], call a transform function)。它是这样工作的: 对于 Args... 中的每个参数,只需在 transformed_args... 中创建相同的类型,除非它是以下之一,在这种情况下进行以下转换:

Type In Args... Type In transformed_Args...
SomeClass shared_ptr to SomeClass
std::vector of SomeClass std::vector of shared_ptr to SomeClass

ex 的其他一切都保持不变: int 仍然是 int std::string 仍然是 std::string

我当然是通过模板专业化来实现的 对于下一部分,我采用 transformed_args...,发布一个 class 和一个仿函数。我收到来自(C++ 使用 Pybind 生成的 Python,但并不重要)的关于此仿函数的回调。 class 的相关位如下所示...

template<typename C, typename...transformed_args..., typename... Args>
class SomeTemplateClass
{
    MethodWrapper<C,void, Args...> func;
    //.....
    void operator()(transformed_args... targs)
    {
        //....
        (*func.wrapped_method_inside)(transform_back_magic(targs)...)   // this is want i want to achieve.
        //transform_back_magic(targs)... is a plaeholder for code that checks if type of args[i]... != type of targs[i]... and then calls a tranform_back specialization on it else just return args[i].val
    }
}

targstransformed_args... 格式,但它们针对的底层 C++ 函数期望 Args...

template<typename... Args, typename... transformed_args, ........whatever else is needed>
transform_back_magic(....)
{
    if(Args[i].type != transformed_args[i].types)
        tranform_back(targs[i]...);
}

tranform_back 函数模板逻辑专门针对不同的情况,所有逻辑都已到位。但是如何根据这个布尔标准调用它正在触及我的 TMP 知识限制。我几周前才开始。


在这里,我列出了到目前为止我创建的内容。 首先这是我需要的伪代码

template<typename C, typename... transformed_args, typename... Args>
class SomeTemplateClass
{
    MethodWrapper<C,void, Args...> func;
    void operator(transformed_args... targs)
    {
        **//In pseudo code, this is what i need**
        Args... params = CreateArgsInstanceFromTransformedArgs(targs);
        (*func.wrapped_method_inside)(params...);
    }
}

在我尝试实现这一点时,到目前为止,我已经决定通过从 targs 复制数据来创建一个 tuple<Args...> 对象(在任何需要的地方进行转换)

void operator(transformed_args... targs)
{
    //....
    

auto mytup = call1(std::tuple<args...>(), std::make_index_sequence<sizeof...(Args)>, 
std::make_tuple(targs...), targs...);
    

// mytup can be std::tuple<Args...>(transform_back(1st_targs), transform_back(2nd_targs)....). Once available i can write some more logic to extract Args... from this tuple and pass to(*func.wrapped_method_inside)(....)
    (*func.wrapped_method_inside)(ArgsExtractorFromTuple(mytup)); // this part is not implemented yet, but i think it should be possible. This is not my primary concern at the moment
}

//call1


 template<typename... Args, typename... Targs, std::size_t... N>
    auto call1(std::tuple<Args...> tupA, std::index_sequence<N>..., std::tuple<Targs...> tupT, Targs ..)
{
    auto booltup = tuple_creator<0>(tupA, tupT, nullptr);   // to create a tuple of bools
    auto ret1 = std::make_tuple<Args...>(call2(booltup, targs, N)...); // targs and N are expanded together so that i get indirect access to see the corresponding type in Args...
    return ret1;
}
// tuple_creator is a recursive function template with sole purpose to create a boolean tuple.
// such that std::get<0>(booltup) = true, 
//if tuple_element_t<0,std::tuple<Args...>> and tuple_element_t<0,std::tuple<targs...>> are same types else false

template<size_t I, typename... Targs, typename... Args>
auto tuple_creator(std::tuple<Args...>tupA, std::tuple<Targs...>tupT, std::enable_if_t<I == sizeof...(targs)>*)
{
    return std::make_tuple(std::is_same<std::tuple_element_t<I-1, std::tuple<Targs...>>, std::tuple_element_t<I-1, std::tuple<Args...>>>::value);
}

template<size_t I = 0, typename... Targs, typename... Args>
auto tuple_creator(std::tuple<Args...>tupA, std::tuple<Targs...>tupT, std::enable_if_t<I < sizeof...(targs)>*)
{
    auto ret1 = tuple_creator<I+1>(tupA, tupT, nullptr);
    if(!I)
        return ret1;
    auto ret2 = std::is_same<std::tuple_element_t<I-1, std::tuple<Targs...>>, std::tuple_element_t<I-1, std::tuple<Args...>>>::value;
    return std::tuple_cat(ret1, std::make_tuple(ret2));
}

template<typename TT, typename Tuple>
auto call2(Tuple boolyup, TT t, std::size_t I)
{
    auto ret = transform_back<std::get<I>(booltup)>(t); // error: I is not a compile time constant
    return ret;
}

transform_back 是一个模板,它使用 bool 模板参数和基于 enable_if 的特化来决定是否将参数转换回

以下是 std::vector 的 transform_back 专业化。同样,我还有其他时间 T = Class 等等

 template<bool sameTypes, typename T>
    std::enable_if_t<(is_vector<T>::value, is_shared_ptr<typename T::value_type>::value && 
is_class<remove_cvref_t<typename T::value_type_element_type>>::value 
&& sameTypes), T>
    transform_back(T val)       // it was never transfoemd in first place, return as is
    {
        return val;
    }
    
    template<bool sameTypes, typename T>
    std::enable_if_t<(is_vector<T>::value, is_shared_ptr<typename T::value_type>::value 
&& is_class<remove_cvref_t<typename T::value_type_element_type>>::value 
&& !sameTypes), 
typename std::vector<typename T::value_type::element_type>>
    transform(T val)
    {
        std::vector<T::value_type::element_type> t;
        for(int i = 0 ; i < val.size(); ++i)
        {
            typename T::value_type::element_type obj = *val[i];
            t.push_back(obj);
        }
        return t;
    }

这两个专业相同,仅在 sameTypes 布尔变量

上有所不同

此代码当前在尝试使用

时在 call2 方法中出错
std::get
auto ret = transform_back<std::get<I>(booltup)>(t); // error: I is not a compile time constant

你能帮上什么忙?

1) 解决 std::get 问题的方法是什么?只是想不出一种方法来适应 std::size_t 作为模板 arg 而不是函数 arg 以使其在编译时工作。 除此之外:

2)如果您可以建议从顶层实施的替代方法。

Args... params = CreateArgsInstanceFromTransformedArgs(targs);

那就太好了。我走的路对我个人来说不是很有说服力。

如果我没理解错的话,你可以这样做:

template <typename> struct Tag{};

std::shared_ptr<SomeClass> transform_to(Tag<std::shared_ptr<SomeClass>>, const SomeClass& s)
{
    return std::make_shared<SomeClass>(s);
}

std::vector<std::shared_ptr<SomeClass>> transform_to(Tag<std::vector<std::shared_ptr<SomeClass>>>, const std::vector<SomeClass>& v)
{
    std::vector<std::shared_ptr<SomeClass>> res;
    res.reserve(v.size());
    for (const auto& s : v) {
        res.emplace_back(std::make_shared<SomeClass>(s));
    }
    return res;
}

const SomeClass& transform_to(Tag<SomeClass>, const std::shared_ptr<SomeClass>& s)
{
    return *s;
}

std::vector<SomeClass> transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
    std::vector<SomeClass> res;
    res.reserve(v.size());
    for (const auto& s : v) {
        res.emplace_back(*s);
    }
    return res;
}

template <typename T>
const T& transform_to(Tag<T>, const T& t) { return t; } // No transformations

然后

std::function<void (Args...)> func;

template <typename ... transformed_args>
void operator () (transformed_args... targs) const
{
    func(transform_to(Tag<Args>(), targs)...);
}

只是在这里解释用例以添加一些上下文。考虑 C++ 中的这三个方法,每个方法都用函数指针 SomeTemplateClass::func:

表示
void foo(vector<shared_ptr<SomeClass>>)             // 1
// Args... = vector<shared_ptr<SomeClass>>, Targs... = vector<shared_ptr<SomeClass>>
void foo(vector<SomeClass>)                         // 2
// Args... = vector<SomeClass>, Targs... = vector<shared_ptr<SomeClass>>
void foo(vector<SomeClass>, vector<shared_ptr<SomeClass>>)  // 3
// Args... = vector<SomeClass>, vector<shared_ptr<SomeClass>>, Targs... = vector<shared_ptr<SomeClass>>, vector<shared_ptr<SomeClass>>

每个 SomeTemplateClass 的一个实例通过 Pybind 暴露给 Python。我进行这些转换,以便当从 Python 调用 foo 时,任何 arg vector<T>(in C++)SomeTemplateClass 函子中作为 vector<shared_ptr<T>> 接收。这有助于处理我需要的先前创建的对象 T

但是从 foo 的 3 个案例可以看出,foo(vector<shared_ptr<T>>) 不需要转换为,随后也不需要转换回来。 'tranform_to'的情况用模板特化很容易处理,但是在转换回来的时候,vector<shared_ptr<T>>不能盲目地转换回vector<T>。因此 (transform(targs...)) 需要额外的逻辑来仅在 targ[i]::type != arg[i]::type

时转换特定的 arg(或 targ)

基于 Jarod 的回答,我更需要这样的东西,在 transform_to 方法中,vector 进一步分为两个可能的模板

template<bool wasOriginallyTransformed>
enable_if<!wasOriginallyTransformed, std::vector<std::shared_ptr<SomeClass>> transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
    
    return v;
}

template<bool wasOriginallyTransformed>
enable_if<!wasOriginallyTransformed, std::vector<<SomeClass> 
transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
     std::vector<SomeClass> res;
    res.reserve(v.size());
    for (const auto& s : v) {
        res.emplace_back(*s);
    }
    return res;
}