This question is related to one on SO a couple of years ago by Georg Fritzsche about transforming a parameter pack (Is it possible to transform the types in a parameter pack?). In the end, the individual types in the parameter pack can be transformed, e.g. by converting to corresponding pointer types.
这个问题与几年前由Georg Fritzsche关于转换参数包的问题有关(可以转换参数包中的类型吗?)。最后,可以转换参数包中的各个类型,例如,通过转换为相应的指针类型。
I am wondering if it is possible to use this technique to write one standard function/functor and a set of wrapper functions (out of one template), so that the wrappers can take parameters of equivalent types and then call the standard function to do actual work.
我想知道是否可以使用这种技术编写一个标准函数/函数和一组包装函数(在一个模板中),这样包装器可以获取等效类型的参数,然后调用标准函数来实现工作。
Using the answer by Johannes Schaub - litb the original example below. Is it possible to write one template f
, which can take any combinations of int/int*,char/char*
and call a common function f_std(int*,char*)
to do the work. (The number of parameters is not pre-specified.)
使用Johannes Schaub的答案 - 点亮下面的原始示例。是否可以编写一个模板f,它可以采用int / int *,char / char *的任意组合,并调用常用函数f_std(int *,char *)来完成工作。 (参数数量未预先指定。)
--- Update --- For example, given int i; char c;
, is it possible to write a caller using pack transformation such that the following works
---更新---例如,给定int i; char c;,是否可以使用pack转换编写调用者,以便以下工作
call_ptr(f_std,i,c);
call_ptr(f_std,&i,c);
call_ptr(f_std,i,&c);
What I've tried so far is listed below (updated to clarify.). Basically, I tried to accept an list of not necessarily pointer types and convert them to pointer types, before making call to a std::function that takes pointer types. But the code does not compile. I don't know how to write a helper function to accept one function with a standard signature, but accept a parameter pack of something else.
我到目前为止所尝试的内容如下所列(更新以澄清。)。基本上,在尝试调用带有指针类型的std :: function之前,我试图接受一个不一定是指针类型的列表并将它们转换为指针类型。但是代码没有编译。我不知道如何编写一个辅助函数来接受一个带有标准签名的函数,但是接受其他东西的参数包。
Thanks in advance
提前致谢
#include <type_traits>
#include <functional>
using namespace std;
template<class... Args> struct X {};
template<class T> struct make_pointer { typedef T* type; };
template<class T> struct make_pointer<T*> { typedef T* type; };
template<template<typename...> class List,
template<typename> class Mod,
typename ...Args>
struct magic {
typedef List<typename Mod<Args>::type...> type;
};
/////////////////
// trying to convert parameter pack to pointers
template<class T> T* make_ptr(T x) { return &x; }
template<class T> T* make_ptr(T* x) { return x; }
template <typename Value, typename ...Args>
class ByPtrFunc
{
public:
typedef typename magic<X, make_pointer, Args...>::type PArgs;
Value operator()(Args... args) { return f(make_ptr(args)...); }
private:
std::function<Value (PArgs...)> _ptr_func;
}; //ByPtrFunc
//helper function to make call
template<typename A, typename ...Args>
static A call_ptr(std::function<A (Args...)> f, Args... args) {
return ByPtrFunc<A, Args...>{f}(args ...);
}
int main() {
typedef magic<X, make_pointer, int*, char>::type A;
typedef X<int*, char*> B;
static_assert(is_same<A, B>::value, ":(");
int i=0; char c='c';
function<int (int* pa,char* pb)> f_std = [](int* pa,char* pb)->int {return *pa + * pb;};
f_std(&i,&c);
//////////////////
//Is the following possible.
call_ptr(f_std,i,c);
call_ptr(f_std,&i,c);
call_ptr(f_std,i,&c);
return 0;
}
3 个解决方案
#1
2
Following may help:
以下可能有所帮助
template <typename T> T* make_pointer(T& t) { return &t; }
template <typename T> T* make_pointer(T* t) { return t; }
template <typename Ret, typename... Args, typename ...Ts>
Ret call_ptr(std::function<Ret (Args*...)> f, Ts&&...args)
{
static_assert(sizeof...(Args) == sizeof...(Ts), "Bad parameters");
f(make_pointer(std::forward<Ts>(args))...);
}
Now, use it:
现在,使用它:
void f_std(int*, char*) { /* Your code */ }
int main(int argc, char *argv[])
{
int i;
char c;
std::function<void (int*, char*)> f1 = f_std;
call_ptr(f1, i, c);
call_ptr(f1, i, &c);
call_ptr(f1, &i, c);
call_ptr(f1, &i, &c);
return 0;
}
#2
2
This answers your question syntax-wise, if I've understood it correctly: yes, it's possible.
如果我理解正确的话,这会在语法上回答你的问题:是的,这是可能的。
// given int or char lvalue, returns its address
template<class T>
T* transform(T& t) {
return &t;
}
// given int* or char*, simply returns the value itself
template<class T>
T* transform(T* t) {
return t;
}
// prints out the address corresponding to each of its arguments
void f_std() {
}
template<class Arg, class... Args>
void f_std(Arg arg, Args... args) {
std::cout << (void*)arg << std::endl;
f_std(args...);
}
// converts int to int*, char to char*, then calls f_std
template<class... Args>
void f(Args... args) {
f_std(transform(args)...);
}
Unfortunately, calling f
will pass int
and char
arguments by value, and hence copy them. To fix this, use perfect forwarding in the definition of f
:
不幸的是,调用f将按值传递int和char参数,因此复制它们。要解决这个问题,请在f的定义中使用完美转发:
template<class... Args>
void f(Args&&... args) {
f_std(transform(std::forward<Args>(args))...);
}
Driver:
int main() {
int x = 1;
char c = 'a';
cout << (void*)&x << endl;
cout << (void*)&c << endl;
f(x, &x, c, &c);
}
Output (example; ran it on my machine just now):
输出(例子;刚才在我的机器上运行):
0x7fff36fb5ebc
0x7fff36fb5ebb
0x7fff36fb5ebc
0x7fff36fb5ebc
0x7fff36fb5ebb
0x7fff36fb5ebb
#3
0
For reference, below is what worked for me, based on the accepted answer @Jarod42 and the type transformation "magic". slightly more general and with added type checking. Turns out type-transformation is simply a pattern expansion.
作为参考,下面是对我有用的,基于接受的答案@ Jarod42和类型转换“魔术”。稍微更一般,并添加类型检查。原来,类型转换只是一种模式扩展。
#include <type_traits>
#include <functional>
#include <iostream>
using namespace std;
/////////////////
// convert parameter pack to pointers
//types
template<class T> struct make_ptr_t { typedef T* type; };
template<class T> struct make_ptr_t<T*> { typedef T* type; };
//values
template<class T> T* make_ptr(T& x) { return &x; }
template<class T> T* make_ptr(T* x) { return x; }
/////////////////////////////////////
// (optional) only for type checking
template<class... Args> struct X {};
template<template<typename...> class List,
template<typename> class Mod,
typename ...Args>
struct magic {
typedef List<typename Mod<Args>::type...> type;
};
//helper function to make call
template<typename A, typename ...PArgs, typename ...Args>
static A call_ptr(std::function<A (PArgs...)> f, Args... args) {
static_assert(is_same<X<PArgs...>,typename magic<X, make_ptr_t, Args...>::type>::value, "Bad parameters for f in call_ptr()"); //type checking
return f(make_ptr(args)...);
}
int main() {
int i=0; char c='c'; string s="c";
function<int (int* pa,char* pb)> f_std = [](int* pa,char* pb)->int {return *pa + * pb;};
f_std(&i,&c);
cout << call_ptr(f_std,i,c) << endl;
cout << call_ptr(f_std,&i,c) << endl;
cout << call_ptr(f_std,i,&c) << endl;
//cout << call_ptr(f_std,i,s) << endl; //complains about bad parameters.
return 0;
}
#1
2
Following may help:
以下可能有所帮助
template <typename T> T* make_pointer(T& t) { return &t; }
template <typename T> T* make_pointer(T* t) { return t; }
template <typename Ret, typename... Args, typename ...Ts>
Ret call_ptr(std::function<Ret (Args*...)> f, Ts&&...args)
{
static_assert(sizeof...(Args) == sizeof...(Ts), "Bad parameters");
f(make_pointer(std::forward<Ts>(args))...);
}
Now, use it:
现在,使用它:
void f_std(int*, char*) { /* Your code */ }
int main(int argc, char *argv[])
{
int i;
char c;
std::function<void (int*, char*)> f1 = f_std;
call_ptr(f1, i, c);
call_ptr(f1, i, &c);
call_ptr(f1, &i, c);
call_ptr(f1, &i, &c);
return 0;
}
#2
2
This answers your question syntax-wise, if I've understood it correctly: yes, it's possible.
如果我理解正确的话,这会在语法上回答你的问题:是的,这是可能的。
// given int or char lvalue, returns its address
template<class T>
T* transform(T& t) {
return &t;
}
// given int* or char*, simply returns the value itself
template<class T>
T* transform(T* t) {
return t;
}
// prints out the address corresponding to each of its arguments
void f_std() {
}
template<class Arg, class... Args>
void f_std(Arg arg, Args... args) {
std::cout << (void*)arg << std::endl;
f_std(args...);
}
// converts int to int*, char to char*, then calls f_std
template<class... Args>
void f(Args... args) {
f_std(transform(args)...);
}
Unfortunately, calling f
will pass int
and char
arguments by value, and hence copy them. To fix this, use perfect forwarding in the definition of f
:
不幸的是,调用f将按值传递int和char参数,因此复制它们。要解决这个问题,请在f的定义中使用完美转发:
template<class... Args>
void f(Args&&... args) {
f_std(transform(std::forward<Args>(args))...);
}
Driver:
int main() {
int x = 1;
char c = 'a';
cout << (void*)&x << endl;
cout << (void*)&c << endl;
f(x, &x, c, &c);
}
Output (example; ran it on my machine just now):
输出(例子;刚才在我的机器上运行):
0x7fff36fb5ebc
0x7fff36fb5ebb
0x7fff36fb5ebc
0x7fff36fb5ebc
0x7fff36fb5ebb
0x7fff36fb5ebb
#3
0
For reference, below is what worked for me, based on the accepted answer @Jarod42 and the type transformation "magic". slightly more general and with added type checking. Turns out type-transformation is simply a pattern expansion.
作为参考,下面是对我有用的,基于接受的答案@ Jarod42和类型转换“魔术”。稍微更一般,并添加类型检查。原来,类型转换只是一种模式扩展。
#include <type_traits>
#include <functional>
#include <iostream>
using namespace std;
/////////////////
// convert parameter pack to pointers
//types
template<class T> struct make_ptr_t { typedef T* type; };
template<class T> struct make_ptr_t<T*> { typedef T* type; };
//values
template<class T> T* make_ptr(T& x) { return &x; }
template<class T> T* make_ptr(T* x) { return x; }
/////////////////////////////////////
// (optional) only for type checking
template<class... Args> struct X {};
template<template<typename...> class List,
template<typename> class Mod,
typename ...Args>
struct magic {
typedef List<typename Mod<Args>::type...> type;
};
//helper function to make call
template<typename A, typename ...PArgs, typename ...Args>
static A call_ptr(std::function<A (PArgs...)> f, Args... args) {
static_assert(is_same<X<PArgs...>,typename magic<X, make_ptr_t, Args...>::type>::value, "Bad parameters for f in call_ptr()"); //type checking
return f(make_ptr(args)...);
}
int main() {
int i=0; char c='c'; string s="c";
function<int (int* pa,char* pb)> f_std = [](int* pa,char* pb)->int {return *pa + * pb;};
f_std(&i,&c);
cout << call_ptr(f_std,i,c) << endl;
cout << call_ptr(f_std,&i,c) << endl;
cout << call_ptr(f_std,i,&c) << endl;
//cout << call_ptr(f_std,i,s) << endl; //complains about bad parameters.
return 0;
}