我正在尝试查找迭代包变量模板参数列表的方法。 现在和所有迭代一样,您需要某种方法来了解打包列表中有多少参数,更重要的是如何从打包参数列表中单独获取数据。
一般的想法是迭代列表,将int类型的所有数据存储到向量中,将char *类型的所有数据存储到向量中,并将float类型的所有数据存储到向量中。在这个过程中,还需要一个单独的向量来存储参数进入顺序的各个字符。例如,当你push_back(a_float)时,你也在做一个push_back('f'),它只是存储一个单独的char来了解数据的顺序。我也可以在这里使用std :: string,只需使用+ =。该矢量仅用作示例。
现在设计事物的方式是功能本身是使用宏构建的,尽管有恶意,但它是必需的,因为这是一个实验。因此,使用递归调用几乎是不可能的,因为将包含所有这些的实际实现将在编译时扩展;你不能招募一个宏。
尽管有各种可能的尝试,我仍然坚持要弄清楚如何实际做到这一点。所以相反,我使用了一个更复杂的方法,包括构造一个类型,并将该类型传递到varadic模板,在一个向量中扩展它,然后简单地迭代它。但是我不想像以下那样调用函数:
foo(arg(1), arg(2.0f), arg("three");
所以真正的问题是如果没有这样的话我该怎么办?为了让你们更好地理解代码实际上在做什么,我已经粘贴了我目前正在使用的乐观方法。
struct any {
void do_i(int e) { INT = e; }
void do_f(float e) { FLOAT = e; }
void do_s(char* e) { STRING = e; }
int INT;
float FLOAT;
char *STRING;
};
template<typename T> struct get { T operator()(const any& t) { return T(); } };
template<> struct get<int> { int operator()(const any& t) { return t.INT; } };
template<> struct get<float> { float operator()(const any& t) { return t.FLOAT; } };
template<> struct get<char*> { char* operator()(const any& t) { return t.STRING; } };
#define def(name) \
template<typename... T> \
auto name (T... argv) -> any { \
std::initializer_list<any> argin = { argv... }; \
std::vector<any> args = argin;
#define get(name,T) get<T>()(args[name])
#define end }
any arg(int a) { any arg; arg.INT = a; return arg; }
any arg(float f) { any arg; arg.FLOAT = f; return arg; }
any arg(char* s) { any arg; arg.STRING = s; return arg; }
我知道这很讨厌,但这是一个纯粹的实验,不会在生产代码中使用。这纯粹是一个想法。它可能是一种更好的方式。但是你将如何使用这个系统的一个例子:
def(foo)
int data = get(0, int);
std::cout << data << std::endl;
end
看起来很像python。它也有效,但唯一的问题是你如何调用这个函数。 下面是一个简单的例子:
foo(arg(1000));
我需要构建一个新的任何类型,这是非常美观的,但不是说那些宏也不是。除此之外,我只想做的选择: FOO(1000);
我知道它可以完成,我只需要某种迭代方法,或者更重要的是一些std :: get方法用于打包的variadic模板参数列表。我确信可以做到。
另外需要注意的是,我很清楚这不是类型友好的,因为我只支持int,float,char *,这对我来说没关系。我不需要任何其他东西,我将添加检查以使用type_traits来验证传递的参数确实是正确的,如果数据不正确则产生编译时错误。这纯粹不是问题。除了这些POD类型之外,我也不需要任何支持。
如果我能得到一些建设性的帮助,反对关于我纯粹不合逻辑和愚蠢地使用宏和仅POD类型的论点,那将是非常高兴的。我很清楚代码是多么脆弱和破碎。这是merley的一个实验,我稍后可以解决非POD数据的问题,并使其更加类型安全和可用。
感谢您的光临,我期待着提供帮助。
答案 0 :(得分:30)
如果要将参数包装到any
,可以使用以下设置。我还使any
类更有用,尽管从技术上讲它不是any
类。
#include <vector>
#include <iostream>
struct any {
enum type {Int, Float, String};
any(int e) { m_data.INT = e; m_type = Int;}
any(float e) { m_data.FLOAT = e; m_type = Float;}
any(char* e) { m_data.STRING = e; m_type = String;}
type get_type() const { return m_type; }
int get_int() const { return m_data.INT; }
float get_float() const { return m_data.FLOAT; }
char* get_string() const { return m_data.STRING; }
private:
type m_type;
union {
int INT;
float FLOAT;
char *STRING;
} m_data;
};
template <class ...Args>
void foo_imp(const Args&... args)
{
std::vector<any> vec = {args...};
for (unsigned i = 0; i < vec.size(); ++i) {
switch (vec[i].get_type()) {
case any::Int: std::cout << vec[i].get_int() << '\n'; break;
case any::Float: std::cout << vec[i].get_float() << '\n'; break;
case any::String: std::cout << vec[i].get_string() << '\n'; break;
}
}
}
template <class ...Args>
void foo(Args... args)
{
foo_imp(any(args)...); //pass each arg to any constructor, and call foo_imp with resulting any objects
}
int main()
{
char s[] = "Hello";
foo(1, 3.4f, s);
}
然而,可以编写函数来访问可变参数模板函数中的第n个参数,并将函数应用于每个参数,这可能是一种更好的方式来做任何你想要实现的事情。
答案 1 :(得分:20)
这不是人们通常使用Variadic模板的方式,而根本不是。
根据语言规则,无法对可变参数包进行迭代,因此您需要转向递归。
class Stock
{
public:
bool isInt(size_t i) { return _indexes.at(i).first == Int; }
int getInt(size_t i) { assert(isInt(i)); return _ints.at(_indexes.at(i).second); }
// push (a)
template <typename... Args>
void push(int i, Args... args) {
_indexes.push_back(std::make_pair(Int, _ints.size()));
_ints.push_back(i);
this->push(args...);
}
// push (b)
template <typename... Args>
void push(float f, Args... args) {
_indexes.push_back(std::make_pair(Float, _floats.size()));
_floats.push_back(f);
this->push(args...);
}
private:
// push (c)
void push() {}
enum Type { Int, Float; };
typedef size_t Index;
std::vector<std::pair<Type,Index>> _indexes;
std::vector<int> _ints;
std::vector<float> _floats;
};
示例(在行动中),假设我们有Stock stock;
:
stock.push(1, 3.2f, 4, 5, 4.2f);
被解析为(a)因为第一个参数是int
this->push(args...)
扩展为this->push(3.2f, 4, 5, 4.2f);
,解析为(b)第一个参数为float
this->push(args...)
扩展为this->push(4, 5, 4.2f);
,解析为(a)第一个参数为int
this->push(args...)
扩展为this->push(5, 4.2f);
,解析为(a)第一个参数为int
this->push(args...)
扩展为this->push(4.2f);
,解析为(b)第一个参数为float
this->push(args...)
扩展为this->push();
,由于没有参数,因此解析为(c),从而结束递归因此:
std::string const&
)Foo
),则不能选择任何重载,从而导致编译时错误。一个警告:自动转换意味着double
会选择重载(b),而short
会选择重载(a)。如果不希望这样,那么需要引入SFINAE,这使得该方法稍微复杂一些(至少是它们的签名),例如:
template <typename T, typename... Args>
typename std::enable_if<is_int<T>::value>::type push(T i, Args... args);
is_int
的位置如下:
template <typename T> struct is_int { static bool constexpr value = false; };
template <> struct is_int<int> { static bool constexpr value = true; };
另一种选择是考虑变体类型。例如:
typedef boost::variant<int, float, std::string> Variant;
它已经存在,有了所有实用程序,它可以存储在vector
中,复制等等......看起来非常像你需要的,即使它不使用Variadic模板。
答案 2 :(得分:17)
您可以通过在{}之间使用参数包初始化它来创建容器。只要params的类型......是同质的或者至少可以转换为容器的元素类型,它就可以工作。 (用g ++ 4.6.1测试)
#include <array>
template <class... Params>
void f(Params... params) {
std::array<int, sizeof...(params)> list = {params...};
}
答案 3 :(得分:10)
目前没有特定的功能,但您可以使用一些解决方法。
一种解决方法使用的事实是,initialization lists的子表达式按顺序进行评估。 int a[] = {get1(), get2()}
会在执行get1
之前执行get2
。也许fold expressions将来会用于类似的技术。要在每个参数上调用do()
,您可以执行以下操作:
template <class... Args>
void doSomething(Args... args) {
int x[] = {args.do()...};
}
但是,这仅在do()
返回int
时才有效。您可以使用comma operator来支持无法返回正确值的操作。
template <class... Args>
void doSomething(Args... args) {
int x[] = {(args.do(), 0)...};
}
要做更复杂的事情,你可以把它们放在另一个功能中:
template <class Arg>
void process(Arg arg, int &someOtherData) {
// You can do something with arg here.
}
template <class... Args>
void doSomething(Args... args) {
int someOtherData;
int x[] = {(process(args, someOtherData), 0)...};
}
请注意,使用泛型lambdas(C ++ 14),您可以定义一个函数来为您执行此样板。
template <class F, class... Args>
void do_for(F f, Args... args) {
int x[] = {(f(args), 0)...};
}
template <class... Args>
void doSomething(Args... args) {
do_for([&](auto arg) {
// You can do something with arg here.
}, args...);
}
另一种可能性是使用递归。这是一个小例子,它定义了与上面类似的函数do_for
。
template <class F, class First, class... Rest>
void do_for(F f, First first, Rest... rest) {
f(first);
do_for(f, rest...);
}
template <class F>
void do_for(F f) {
// Parameter pack is empty.
}
template <class... Args>
void doSomething(Args... args) {
do_for([&](auto arg) {
// You can do something with arg here.
}, args...);
}
答案 4 :(得分:10)
这确实是 JojOatXGME 的答案的改进(我通过研究他们的答案来弄清楚)。这对于我不使用外部函数(c ++ 17)的混合类型输入最有效:
#include <iostream>
template <typename ... T>
void Foo (T && ... multi_inputs)
{
int i = 0;
([&] (auto & input)
{
// Do things in your "loop" lambda
++i;
std::cout << "input " << i << " = " << input << std::endl;
} (multi_inputs), ...);
}
int main()
{
Foo(2, 3, 4u, (int64_t) 9, 'a', 2.3);
return 0;
}
g++ -std=c++17
的输出:
输入1 = 2
输入2 = 3
输入3 = 4
输入4 = 9
输入5 = a
输入6 = 2.3
如果您想要一种中断条件或在“循环”中返回,那么这是一种解决方法:
struct BREAK {};
template <typename ... T>
bool Foo (T && ... multi_inputs)
{
int i = 0;
auto loop = [&] (auto & input)
{
// Do things in your "loop" lambda
++i;
std::cout << "input " << i << " = " << input << std::endl;
// some conditional breaks / returns
if (input == 9)
throw BREAK();
if (input < 0)
throw false;
if (input == 'a')
throw true;
};
try
{
(loop(multi_inputs), ...);
}
catch (BREAK) {}
catch (bool return_val)
{
return return_val;
}
// more post-loop code
return true;
}
第二个答案可能是代码味道,但表明它是通用的。另外,如果您想要完美的转发,请更改为以下内容:
(auto && input) // double "&" now
// ...
(std::forward<T>(multi_inputs)), ...);
答案 5 :(得分:5)
基于循环的范围非常棒:
#include <iostream>
#include <any>
template <typename... Things>
void printVariadic(Things... things) {
for(const auto p : {things...}) {
std::cout << p.type().name() << std::endl;
}
}
int main() {
printVariadic(std::any(42), std::any('?'), std::any("C++"));
}
对我来说,this会产生输出:
i
c
PKc
Here是一个没有std::any
的示例,对于那些不熟悉std::type_info
的人来说可能更容易理解:
#include <iostream>
template <typename... Things>
void printVariadic(Things... things) {
for(const auto p : {things...}) {
std::cout << p << std::endl;
}
}
int main() {
printVariadic(1, 2, 3);
}
正如您所料,这会产生:
1
2
3
答案 6 :(得分:3)
你不能迭代,但你可以递归列表。检查维基百科上的printf()示例:http://en.wikipedia.org/wiki/C++0x#Variadic_templates
答案 7 :(得分:1)
#include <iostream>
template <typename Fun>
void iteratePack(const Fun&) {}
template <typename Fun, typename Arg, typename ... Args>
void iteratePack(const Fun &fun, Arg &&arg, Args&& ... args)
{
fun(std::forward<Arg>(arg));
iteratePack(fun, std::forward<Args>(args)...);
}
template <typename ... Args>
void test(const Args& ... args)
{
iteratePack([&](auto &arg)
{
std::cout << arg << std::endl;
},
args...);
}
int main()
{
test(20, "hello", 40);
return 0;
}
输出:
20
hello
40
答案 8 :(得分:0)
你可以使用多个可变参数模板,这有点乱,但它的工作原理很容易理解。 你只需要一个带有可变参数模板的函数,如下所示:
template <typename ...ArgsType >
void function(ArgsType... Args){
helperFunction(Args...);
}
这样的辅助函数:
void helperFunction() {}
template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args) {
//do what you want with t
function(Args...);
}
现在当你打电话&#34;功能&#34; &#34; helperFunction&#34;将调用并将第一个传递的参数与其余参数隔离,此变量可用于调用另一个函数(或其他东西)。然后&#34;功能&#34;将一次又一次地调用,直到不再有变量为止。请注意,您可能必须在&#34; function&#34;。
之前声明helperClass最终代码如下所示:
void helperFunction();
template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args);
template <typename ...ArgsType >
void function(ArgsType... Args){
helperFunction(Args...);
}
void helperFunction() {}
template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args) {
//do what you want with t
function(Args...);
}
代码未经过测试。