I want to create somewhat sibling of std::fscanf()
(I know it's a C function). So, my interface is something like this:
template <charT, char_traits, ...>
std::size_t ts_scanf(is, format, opening_bracket, closing_bracket, args)
I decided to implement a C# version of reading from console, since it requires programmer to maintain only one sequence (the args part), not the arguments and format.
Here is how C# version works:
"text blah blah blah {0} {1} {0}", arg1, arg2
So, it infers types of arg1, arg2, and then reads the text in location where {N} stands into the appropriate argument.
Algorithm of what I want to do:
1.Find opening bracket
2.Try to parse an int, say N
3.if succeeded, get the Nth parameter from the pack, read into it using is>>get<N>args
.
4.if failed,perform dumb read
5.Repeat 1 to 4 until the end of the format or until stream exhausts
So, when writing a loop I encountered a problem :
for (i = 0; i < length; i = format.find(i, opening_bracket))
I found that I need to somehow expand the parameter pack args
, which is impossible to do at runtime (since the loop is runtime). The only solution I have in mind is recurse: when finding opening bracket, read it, trim the format string, and recurse with the trimmed string and the rest of the variadic pack.
Question: is there a solution where it would be possible to expand the variadic pack at (pseudo) runtime?
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
gives you unpacked indexes.
for_each
calls f
with a compile time i
value for each i
up to N
.
That will let you iterate over integers at compile time. To map integers at runtime to compile time:
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);
} );
}
This invokes f
with a compile time version of I
so long as it is less than 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);
} );
}
}
Now there is an implicit chain of if
s written by the above; you can replace with a jump table using a different technique.
Code not compiled; design is sound, but there are probably tyops. Indexer can be found with google (I have written it on SO before). I have written it as for_each
directly, but I find the single pack version too useful. Here I needed the separate pack version. Pick just uses it.
Here is a jump table version of pick:
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);
});
}
Bounds checking not included. This version does not require for_each
, but some compilers break when asked o have a lambda with a parameter pack unexpanded inside a statement.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.