2016-11-12 32 views
1

我想创建std::fscanf()(我知道这是一个C函数)的兄弟姐妹。所以,我的界面是这样的:如何在(伪?)运行时期间迭代variadic模板包?

template <charT, char_traits, ...> 
std::size_t ts_scanf(is, format, opening_bracket, closing_bracket, args) 

我决定实行从控制台读取C#版本,因为它需要程序员只保留一个序列(ARGS部分),没有的参数和格式。

这里是如何C#版本作品:

"text blah blah blah {0} {1} {0}", arg1, arg2 

因此,它推断类型ARG1,ARG2,然后读取位置文本其中{N}代表到相应的参数。

什么,我想要做的算法

1.Find开括号

2.Try解析一个int,说ñ

3.如果成功了,得到的第N个参数从包中读入is>>get<N>args

4.如果失败,执行哑读

5.重复1〜4,直到格式的结束,或直至流耗尽

所以,写一个循环,当我遇到问题

for (i = 0; i < length; i = format.find(i, opening_bracket)) 

我发现我需要以某种方式扩展参数包args,这在运行时是不可能的(因为循环是运行时)。我唯一想到的解决方法是递归:找到左括号,阅读它,修剪格式字符串,然后用修剪过的字符串和可变参数包的剩余部分进行递归。

问题:是否有解决方案,可以在(伪)运行时扩展可变包?

+0

什么是'opening_bracket'和'closing_bracket'?你为什么需要这个? 'fscanf'没有这样的AFAIK。一个关于你想如何工作的伪代码在解释方面会花费很长时间,而不是用言语表达。 – Arunmu

+0

@Arunmu,谢谢,加入 – Incomputable

+0

如果你想真正有效地做到这一点,那么你应该使用表达式模板来在编译时自己完成它(映射)。这可以使用'boost :: proto'来完成,但我承认它不适合弱者:)。我知道的另一个解决方案只是纯粹的运行时,因为你只是在运行时解析括号。这将涉及将参数存储在运行时容器中。 – Arunmu

回答

2
template<class=void, std::size_t...Is > 
auto indexer(std::index_sequence<Is...>){ 
    return [](auto&&f)->decltype(auto){ 
    return decltype(f)(f)(std::integral_constant<std::size_t, Is>{}...); 
    }; 
} 
template<std::size_t N> 
auto indexer(){ 
    return indexer(std::make_index_sequence<N>{}); 
} 
template<std::size_t N> 
void for_each(F&& f) { 
    indexer<N>()([&](auto...Is){ 
    using discard=int[]; 
    (void)discard{0,(void(
     f(Is) 
    ),0)...}; 
    }); 
} 

indexer为您提供了未压缩的索引。

for_each调用f,编译时间为i,每个值为i,最大值为N

这将让你在编译时遍历整数。要在运行时映射整数编译时间:

template<std::size_t N, class F> 
void pick(std::size_t I, F&& f){ 
    for_each<N>([&](auto i){ 
    if (I==i) f(i); 
    }); 
} 

这将调用fI编译时的版本,只要它小于N

template<class...Args> 
void read(std::string pattern, Args&...args){ 
    auto tied=std::tie(args...); 
    for (i = 0; i < length; i = format.find(i, opening_bracket)) 
    pick<sizeof...(args)>(i, [&](auto i){ 
     std::cin>>std::get<i>(tied); 
    }); 
    } 
} 

现在有一个由上面所写的if的隐含链;您可以使用不同的技术替换跳转表。

代码未编译;设计是完善的,但有可能是特洛伊。索引器可以找到与谷歌(我已经写在SO之前)。我直接把它写成for_each,但是我发现单包版本太有用了。这里我需要单独的包装版本。挑选使用它。

这里是挑一个跳转表版本:

template<std::size_t N, class F> 
void pick(std::size_t I, F&& f){ 
    indexer<N>()([&](auto...Is){ 
    using table_f=void(*)(&f); 
    const table_f table[]={ 
     +[](F&f){ f(decltype(Is){}); }... 
    }; 
    table[I](f); 
    }); 
} 

边界检查不包括在内。这个版本不需要for_each,但是有些编译器在被问到有一个lambda的参数包在一个语句内未展开的时候会中断。

+1

感谢Template-Yoda。 –