根据布尔标准转换每个参数包的值
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
}
}
targs
是 transformed_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;
}
我试图在 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
}
}
targs
是 transformed_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
基于 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;
}