if constexpr with recursive parameter packsC++11 make_pair with specified template parameters doesn't compileDifference between `constexpr` and `const`C++: No matching function call when calling tuple_transpose functionIs array indexing constexpr or not ? GCC inconsistent?Enable method based on boolean template parameterImplementing is_constexpr_copiableC++ Template recursive to check type in std::tupleSize of std::array in class template depending on template parameterWhy recursive constexpr template value does not compile?Why did this error occur? c++ library build
Why does the Saturn V have standalone inter-stage rings?
Is there a way, while dragging, to "snap" to the nearest guide?
How does DC work with natural 20?
Is a single radon-daughter atom in air a solid?
Can humans ever directly see a few photons at a time? Can a human see a single photon?
Suggested order for Amazon Prime Doctor Who series
Hot coffee brewing solutions for deep woods camping
How large would a mega structure have to be to host 1 billion people indefinitely?
Can Ogre clerics use Purify Food and Drink on humanoid characters?
Methodology: Writing unit tests for another developer
When can you leave off “le/la” to say “it” in French?
Explain why a line can never intersect a plane in exactly two points.
Do I have to explain the mechanical superiority of the player-character within the fiction of the game?
Is it illegal to withhold someone's passport and green card in California?
How to model a twisted cylinder like this
What was the Shuttle Carrier Aircraft escape tunnel?
Why do some professors with PhDs leave their professorships to teach high school?
Find the C-factor of a vote
Are all instances of trolls turning to stone ultimately references back to Tolkien?
What is "industrial ethernet"?
What's currently blocking the construction of the wall between Mexico and the US?
Why are < or > required to use /dev/tcp
How to find the last non zero element in every column throughout dataframe?
What is the origin of Scooby-Doo's name?
if constexpr with recursive parameter packs
C++11 make_pair with specified template parameters doesn't compileDifference between `constexpr` and `const`C++: No matching function call when calling tuple_transpose functionIs array indexing constexpr or not ? GCC inconsistent?Enable method based on boolean template parameterImplementing is_constexpr_copiableC++ Template recursive to check type in std::tupleSize of std::array in class template depending on template parameterWhy recursive constexpr template value does not compile?Why did this error occur? c++ library build
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I am trying to recursively run through a typelist so I can do some run-time code based on each type in the list. I would like to be able to recursively run through all the types in a tuple in a function in a struct (not in a function in the struct) without using "if constexpr" to terminate the recursion.
I have a snippet of code the shows the recursion working with constexpr.
#include <iostream>
#include <string>
#include <tuple>
template <typename ...Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this()
_inner_print<_N,_N>();
template <std::size_t N, std::size_t MAX>
void _inner_print()
if constexpr ( N != 0 )
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
TypeList _mem;
;
int main()
std::string name;
temp<int, int, int> t1;
t1.print_this();
I would like to be able to do the same recursion with C++14, instead of C++17 w/ "if constexpr".
Thank you!
c++
add a comment |
I am trying to recursively run through a typelist so I can do some run-time code based on each type in the list. I would like to be able to recursively run through all the types in a tuple in a function in a struct (not in a function in the struct) without using "if constexpr" to terminate the recursion.
I have a snippet of code the shows the recursion working with constexpr.
#include <iostream>
#include <string>
#include <tuple>
template <typename ...Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this()
_inner_print<_N,_N>();
template <std::size_t N, std::size_t MAX>
void _inner_print()
if constexpr ( N != 0 )
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
TypeList _mem;
;
int main()
std::string name;
temp<int, int, int> t1;
t1.print_this();
I would like to be able to do the same recursion with C++14, instead of C++17 w/ "if constexpr".
Thank you!
c++
Something new to learn for me!
– RC0993
Jun 5 at 4:24
constexpr static std::size_t _N = sizeof...(Ts);is a simpler way to achieve the same... Be aware, though, that identifers starting with underscore followed by capital letter are reserved (as well as those containing two underscores).
– Aconcagua
Jun 5 at 5:03
add a comment |
I am trying to recursively run through a typelist so I can do some run-time code based on each type in the list. I would like to be able to recursively run through all the types in a tuple in a function in a struct (not in a function in the struct) without using "if constexpr" to terminate the recursion.
I have a snippet of code the shows the recursion working with constexpr.
#include <iostream>
#include <string>
#include <tuple>
template <typename ...Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this()
_inner_print<_N,_N>();
template <std::size_t N, std::size_t MAX>
void _inner_print()
if constexpr ( N != 0 )
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
TypeList _mem;
;
int main()
std::string name;
temp<int, int, int> t1;
t1.print_this();
I would like to be able to do the same recursion with C++14, instead of C++17 w/ "if constexpr".
Thank you!
c++
I am trying to recursively run through a typelist so I can do some run-time code based on each type in the list. I would like to be able to recursively run through all the types in a tuple in a function in a struct (not in a function in the struct) without using "if constexpr" to terminate the recursion.
I have a snippet of code the shows the recursion working with constexpr.
#include <iostream>
#include <string>
#include <tuple>
template <typename ...Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this()
_inner_print<_N,_N>();
template <std::size_t N, std::size_t MAX>
void _inner_print()
if constexpr ( N != 0 )
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
TypeList _mem;
;
int main()
std::string name;
temp<int, int, int> t1;
t1.print_this();
I would like to be able to do the same recursion with C++14, instead of C++17 w/ "if constexpr".
Thank you!
c++
c++
asked Jun 5 at 4:01
mrieramriera
725
725
Something new to learn for me!
– RC0993
Jun 5 at 4:24
constexpr static std::size_t _N = sizeof...(Ts);is a simpler way to achieve the same... Be aware, though, that identifers starting with underscore followed by capital letter are reserved (as well as those containing two underscores).
– Aconcagua
Jun 5 at 5:03
add a comment |
Something new to learn for me!
– RC0993
Jun 5 at 4:24
constexpr static std::size_t _N = sizeof...(Ts);is a simpler way to achieve the same... Be aware, though, that identifers starting with underscore followed by capital letter are reserved (as well as those containing two underscores).
– Aconcagua
Jun 5 at 5:03
Something new to learn for me!
– RC0993
Jun 5 at 4:24
Something new to learn for me!
– RC0993
Jun 5 at 4:24
constexpr static std::size_t _N = sizeof...(Ts); is a simpler way to achieve the same... Be aware, though, that identifers starting with underscore followed by capital letter are reserved (as well as those containing two underscores).– Aconcagua
Jun 5 at 5:03
constexpr static std::size_t _N = sizeof...(Ts); is a simpler way to achieve the same... Be aware, though, that identifers starting with underscore followed by capital letter are reserved (as well as those containing two underscores).– Aconcagua
Jun 5 at 5:03
add a comment |
4 Answers
4
active
oldest
votes
The trick is to use index_sequence.
Here is a C++14 working solution, improved using @MartinMorterol suggestion.
// -*- compile-command: "g++ -Wall -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() _inner_print(std::make_index_sequence<_N>());
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
auto dummy = 0, (_inner_print<IDX>(),0)...;
(void)dummy;
template <std::size_t IDX>
void _inner_print()
std::cout << "nCall #" << IDX
<< " sizeof " << sizeof(std::get<IDX>(_mem));
TypeList _mem;
;
int main()
std::string name;
temp<int, double, char> t1;
t1.print_this();
which prints:
g++ -Wall -std=c++14 poub.cpp; ./a.out
Call #0 sizeof 4
Call #1 sizeof 8
Call #2 sizeof 1
My initial answer (using recursion)
// -*- compile-command: "g++ -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() _inner_print(std::make_index_sequence<_N>());
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
_inner_print(std::integral_constant<std::size_t, IDX>()...);
template <std::size_t HEAD_IDX, typename... TAIL>
void _inner_print(std::integral_constant<std::size_t, HEAD_IDX>, TAIL... tail)
std::cout << "nCall #" << HEAD_IDX
<< " sizeof " << sizeof(std::get<HEAD_IDX>(_mem));
// whatever you want HERE ...
_inner_print(tail...);
void _inner_print();
TypeList _mem;
;
int main()
std::string name;
temp<int, double, char> t1;
t1.print_this();
You may use the trickauto dummy = (_inner_print<IDX>(),0)...;to do your C++17 code in C++14. And since your solution don't recurse, it probably compile faster !
– Martin Morterol
Jun 5 at 5:29
@MartinMorterol, great, that works! Are you ok if I update my answer using your suggestion?
– Picaud Vincent
Jun 5 at 5:36
Yeah sure! Better stack answer => better C++ developer ;)
– Martin Morterol
Jun 5 at 6:33
@MartinMorterol thanks for this C++14 trick I did not know :)
– Picaud Vincent
Jun 5 at 6:35
1
+1 for the array expansion trick. It's an important idiom to get used to when working with variadics, as it simplifies code quite a bit compared to recursion. The important point to remember is that the order of function calls is guaranteed here, which allows scheduling functions with side-effects; it would not be the case with a regular function call as the order of evaluation of function arguments is not guaranteed (and typically MSVC and gcc use different orders).
– Matthieu M.
Jun 5 at 15:01
|
show 4 more comments
If you can change your _inner_print function to a class, you can make use of a partial specialization to end the recursion:
template <std::size_t N, std::size_t MAX>
struct _inner_print
_inner_print()
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
;
template <std::size_t MAX> struct _inner_print<0, MAX> ;
Rather than calling _inner_print() as a function, it becomes a declaration for an unnamed temporary, invoking the constructor that performs your output.
perhapsconstexpr _inner_print()
– Swift - Friday Pie
Jun 5 at 4:38
add a comment |
You should use partial specialization. But you can't do this with a function.
You should use a struct to do the trick.
#include <iostream>
#include <string>
#include <tuple>
template <std::size_t N, std::size_t MAX, class T>
struct inner_print_impl
static void run(const T& caller)
std::cout << "Call #"<<MAX-N<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
inner_print_impl<N-1, MAX , T>::run(caller);
;
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>
static void run(const T& caller)
std::cout << "Call #"<<MAX<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
// no recursion
;
template <typename ...Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t N_ = std::tuple_size<TypeList>::value;
template <std::size_t N, std::size_t MAX, class T>
friend struct inner_print_impl;
void print_this()
inner_print_impl<N_,N_, temp<Ts...> >::run(*this);
TypeList _mem;
private :
int a ; // test acces
;
int main()
std::string name;
temp<int, int, int> t1;
t1.print_this();
Note :
- If you need to acces to a private membre, you will need to pass
*thisto the call and add the newstructas friend of your class - In my code, I have a duplication on the
/* other dynamic code */part. You may :- Use a function
- make your template parameter a
intinstead ofsize_tand stop at-1rather than0
PS :
I don't get the part
in a tuple in a function in a struct (not in a function in the struct)
I hope I didn't miss something
Edit :
My code do one more iteration than your, you may just empty this :
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>
static void run(const T& caller)
;
And you don't display for the 0 case.
add a comment |
template<class Integral, Integral N>
auto dispatch( std::integral_constant<Integral, N> )
return [](auto&&...args)
return std::get<N>( std::forward_as_tuple( decltype(args)(args)... ) );
;
template<std::size_t N>
auto dispatch()
return dispatch( std::integral_constant<std::size_t, N> );
this little beauty gives you much of the magic of if constexpr in c++14.
template <std::size_t N, std::size_t MAX>
void _inner_print()
dispatch( std::integral_constant<bool, N==0> )
(
// 0, aka false branch:
[&](auto Nval)
std::cout << "Call #"<<MAX-Nval<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<Nval-1, MAX>();
,
// 1, aka true branch:
[](auto&&)
)( std::integral_constant<std::size_t, N> );
Live example.
Note that we have to pass in the N via an argument to the lambda, because we want the validity of the lambda's body to vary based on its argument, which is only passed to the right one.
Alternatively, you can use an overload helper:
template<class T0, class...Ts>
struct overloaded;
template<class Lhs, class Rhs, class...Ts>
struct overloaded<Lhs, Rhs, Ts...>:
overloaded<Lhs>, overloaded<Rhs,Ts...>
using overloaded<Lhs>::operator();
using overloaded<Rhs,Ts...>::operator();
template<class A0, class...As>
explicit overloaded(A0&&a0, As&&...as) :
overloaded<Lhs>(std::forward<A0>(a0)),
overloaded<Rhs,Ts...>( std::forward<As>(as)... )
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class T0>
struct overloaded<T0> : T0
using T0::operator();
template<class A0>
explicit overloaded(A0&&a0):
T0(std::forward<A0>(a0))
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class R, class...Args>
struct overloaded<R(*)(Args...)>
R operator()(Args... args) const
return pf(std::forward<Args>(args)...);
R(*pf)(Args...);
overloaded( R(*f)(Args...) ):pf(f)
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class...Args>
auto overload( Args&&...args )
return overloaded<std::decay_t<Args>...>(
std::forward<Args>(args)...
);
Here is how you can use overloaded:
template <std::size_t N, std::size_t MAX>
void _inner_print()
std::integral_constant<std::size_t, N> index;
overload(
[](std::integral_constant<std::size_t, 0>) ,
[&](auto index)
std::cout << "Call #"<<MAX-index<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<index-1, MAX>();
)(index);
note that the rule is that the only-sometimes-compiling dynamic code must depend on the auto arguments to the lambda.
Live example.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56454091%2fif-constexpr-with-recursive-parameter-packs%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
The trick is to use index_sequence.
Here is a C++14 working solution, improved using @MartinMorterol suggestion.
// -*- compile-command: "g++ -Wall -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() _inner_print(std::make_index_sequence<_N>());
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
auto dummy = 0, (_inner_print<IDX>(),0)...;
(void)dummy;
template <std::size_t IDX>
void _inner_print()
std::cout << "nCall #" << IDX
<< " sizeof " << sizeof(std::get<IDX>(_mem));
TypeList _mem;
;
int main()
std::string name;
temp<int, double, char> t1;
t1.print_this();
which prints:
g++ -Wall -std=c++14 poub.cpp; ./a.out
Call #0 sizeof 4
Call #1 sizeof 8
Call #2 sizeof 1
My initial answer (using recursion)
// -*- compile-command: "g++ -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() _inner_print(std::make_index_sequence<_N>());
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
_inner_print(std::integral_constant<std::size_t, IDX>()...);
template <std::size_t HEAD_IDX, typename... TAIL>
void _inner_print(std::integral_constant<std::size_t, HEAD_IDX>, TAIL... tail)
std::cout << "nCall #" << HEAD_IDX
<< " sizeof " << sizeof(std::get<HEAD_IDX>(_mem));
// whatever you want HERE ...
_inner_print(tail...);
void _inner_print();
TypeList _mem;
;
int main()
std::string name;
temp<int, double, char> t1;
t1.print_this();
You may use the trickauto dummy = (_inner_print<IDX>(),0)...;to do your C++17 code in C++14. And since your solution don't recurse, it probably compile faster !
– Martin Morterol
Jun 5 at 5:29
@MartinMorterol, great, that works! Are you ok if I update my answer using your suggestion?
– Picaud Vincent
Jun 5 at 5:36
Yeah sure! Better stack answer => better C++ developer ;)
– Martin Morterol
Jun 5 at 6:33
@MartinMorterol thanks for this C++14 trick I did not know :)
– Picaud Vincent
Jun 5 at 6:35
1
+1 for the array expansion trick. It's an important idiom to get used to when working with variadics, as it simplifies code quite a bit compared to recursion. The important point to remember is that the order of function calls is guaranteed here, which allows scheduling functions with side-effects; it would not be the case with a regular function call as the order of evaluation of function arguments is not guaranteed (and typically MSVC and gcc use different orders).
– Matthieu M.
Jun 5 at 15:01
|
show 4 more comments
The trick is to use index_sequence.
Here is a C++14 working solution, improved using @MartinMorterol suggestion.
// -*- compile-command: "g++ -Wall -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() _inner_print(std::make_index_sequence<_N>());
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
auto dummy = 0, (_inner_print<IDX>(),0)...;
(void)dummy;
template <std::size_t IDX>
void _inner_print()
std::cout << "nCall #" << IDX
<< " sizeof " << sizeof(std::get<IDX>(_mem));
TypeList _mem;
;
int main()
std::string name;
temp<int, double, char> t1;
t1.print_this();
which prints:
g++ -Wall -std=c++14 poub.cpp; ./a.out
Call #0 sizeof 4
Call #1 sizeof 8
Call #2 sizeof 1
My initial answer (using recursion)
// -*- compile-command: "g++ -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() _inner_print(std::make_index_sequence<_N>());
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
_inner_print(std::integral_constant<std::size_t, IDX>()...);
template <std::size_t HEAD_IDX, typename... TAIL>
void _inner_print(std::integral_constant<std::size_t, HEAD_IDX>, TAIL... tail)
std::cout << "nCall #" << HEAD_IDX
<< " sizeof " << sizeof(std::get<HEAD_IDX>(_mem));
// whatever you want HERE ...
_inner_print(tail...);
void _inner_print();
TypeList _mem;
;
int main()
std::string name;
temp<int, double, char> t1;
t1.print_this();
You may use the trickauto dummy = (_inner_print<IDX>(),0)...;to do your C++17 code in C++14. And since your solution don't recurse, it probably compile faster !
– Martin Morterol
Jun 5 at 5:29
@MartinMorterol, great, that works! Are you ok if I update my answer using your suggestion?
– Picaud Vincent
Jun 5 at 5:36
Yeah sure! Better stack answer => better C++ developer ;)
– Martin Morterol
Jun 5 at 6:33
@MartinMorterol thanks for this C++14 trick I did not know :)
– Picaud Vincent
Jun 5 at 6:35
1
+1 for the array expansion trick. It's an important idiom to get used to when working with variadics, as it simplifies code quite a bit compared to recursion. The important point to remember is that the order of function calls is guaranteed here, which allows scheduling functions with side-effects; it would not be the case with a regular function call as the order of evaluation of function arguments is not guaranteed (and typically MSVC and gcc use different orders).
– Matthieu M.
Jun 5 at 15:01
|
show 4 more comments
The trick is to use index_sequence.
Here is a C++14 working solution, improved using @MartinMorterol suggestion.
// -*- compile-command: "g++ -Wall -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() _inner_print(std::make_index_sequence<_N>());
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
auto dummy = 0, (_inner_print<IDX>(),0)...;
(void)dummy;
template <std::size_t IDX>
void _inner_print()
std::cout << "nCall #" << IDX
<< " sizeof " << sizeof(std::get<IDX>(_mem));
TypeList _mem;
;
int main()
std::string name;
temp<int, double, char> t1;
t1.print_this();
which prints:
g++ -Wall -std=c++14 poub.cpp; ./a.out
Call #0 sizeof 4
Call #1 sizeof 8
Call #2 sizeof 1
My initial answer (using recursion)
// -*- compile-command: "g++ -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() _inner_print(std::make_index_sequence<_N>());
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
_inner_print(std::integral_constant<std::size_t, IDX>()...);
template <std::size_t HEAD_IDX, typename... TAIL>
void _inner_print(std::integral_constant<std::size_t, HEAD_IDX>, TAIL... tail)
std::cout << "nCall #" << HEAD_IDX
<< " sizeof " << sizeof(std::get<HEAD_IDX>(_mem));
// whatever you want HERE ...
_inner_print(tail...);
void _inner_print();
TypeList _mem;
;
int main()
std::string name;
temp<int, double, char> t1;
t1.print_this();
The trick is to use index_sequence.
Here is a C++14 working solution, improved using @MartinMorterol suggestion.
// -*- compile-command: "g++ -Wall -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() _inner_print(std::make_index_sequence<_N>());
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
auto dummy = 0, (_inner_print<IDX>(),0)...;
(void)dummy;
template <std::size_t IDX>
void _inner_print()
std::cout << "nCall #" << IDX
<< " sizeof " << sizeof(std::get<IDX>(_mem));
TypeList _mem;
;
int main()
std::string name;
temp<int, double, char> t1;
t1.print_this();
which prints:
g++ -Wall -std=c++14 poub.cpp; ./a.out
Call #0 sizeof 4
Call #1 sizeof 8
Call #2 sizeof 1
My initial answer (using recursion)
// -*- compile-command: "g++ -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
void print_this() _inner_print(std::make_index_sequence<_N>());
template <std::size_t... IDX>
void _inner_print(std::index_sequence<IDX...>)
_inner_print(std::integral_constant<std::size_t, IDX>()...);
template <std::size_t HEAD_IDX, typename... TAIL>
void _inner_print(std::integral_constant<std::size_t, HEAD_IDX>, TAIL... tail)
std::cout << "nCall #" << HEAD_IDX
<< " sizeof " << sizeof(std::get<HEAD_IDX>(_mem));
// whatever you want HERE ...
_inner_print(tail...);
void _inner_print();
TypeList _mem;
;
int main()
std::string name;
temp<int, double, char> t1;
t1.print_this();
edited Jun 5 at 9:53
Jarod42
124k12109194
124k12109194
answered Jun 5 at 5:07
Picaud VincentPicaud Vincent
4,25811138
4,25811138
You may use the trickauto dummy = (_inner_print<IDX>(),0)...;to do your C++17 code in C++14. And since your solution don't recurse, it probably compile faster !
– Martin Morterol
Jun 5 at 5:29
@MartinMorterol, great, that works! Are you ok if I update my answer using your suggestion?
– Picaud Vincent
Jun 5 at 5:36
Yeah sure! Better stack answer => better C++ developer ;)
– Martin Morterol
Jun 5 at 6:33
@MartinMorterol thanks for this C++14 trick I did not know :)
– Picaud Vincent
Jun 5 at 6:35
1
+1 for the array expansion trick. It's an important idiom to get used to when working with variadics, as it simplifies code quite a bit compared to recursion. The important point to remember is that the order of function calls is guaranteed here, which allows scheduling functions with side-effects; it would not be the case with a regular function call as the order of evaluation of function arguments is not guaranteed (and typically MSVC and gcc use different orders).
– Matthieu M.
Jun 5 at 15:01
|
show 4 more comments
You may use the trickauto dummy = (_inner_print<IDX>(),0)...;to do your C++17 code in C++14. And since your solution don't recurse, it probably compile faster !
– Martin Morterol
Jun 5 at 5:29
@MartinMorterol, great, that works! Are you ok if I update my answer using your suggestion?
– Picaud Vincent
Jun 5 at 5:36
Yeah sure! Better stack answer => better C++ developer ;)
– Martin Morterol
Jun 5 at 6:33
@MartinMorterol thanks for this C++14 trick I did not know :)
– Picaud Vincent
Jun 5 at 6:35
1
+1 for the array expansion trick. It's an important idiom to get used to when working with variadics, as it simplifies code quite a bit compared to recursion. The important point to remember is that the order of function calls is guaranteed here, which allows scheduling functions with side-effects; it would not be the case with a regular function call as the order of evaluation of function arguments is not guaranteed (and typically MSVC and gcc use different orders).
– Matthieu M.
Jun 5 at 15:01
You may use the trick
auto dummy = (_inner_print<IDX>(),0)...; to do your C++17 code in C++14. And since your solution don't recurse, it probably compile faster !– Martin Morterol
Jun 5 at 5:29
You may use the trick
auto dummy = (_inner_print<IDX>(),0)...; to do your C++17 code in C++14. And since your solution don't recurse, it probably compile faster !– Martin Morterol
Jun 5 at 5:29
@MartinMorterol, great, that works! Are you ok if I update my answer using your suggestion?
– Picaud Vincent
Jun 5 at 5:36
@MartinMorterol, great, that works! Are you ok if I update my answer using your suggestion?
– Picaud Vincent
Jun 5 at 5:36
Yeah sure! Better stack answer => better C++ developer ;)
– Martin Morterol
Jun 5 at 6:33
Yeah sure! Better stack answer => better C++ developer ;)
– Martin Morterol
Jun 5 at 6:33
@MartinMorterol thanks for this C++14 trick I did not know :)
– Picaud Vincent
Jun 5 at 6:35
@MartinMorterol thanks for this C++14 trick I did not know :)
– Picaud Vincent
Jun 5 at 6:35
1
1
+1 for the array expansion trick. It's an important idiom to get used to when working with variadics, as it simplifies code quite a bit compared to recursion. The important point to remember is that the order of function calls is guaranteed here, which allows scheduling functions with side-effects; it would not be the case with a regular function call as the order of evaluation of function arguments is not guaranteed (and typically MSVC and gcc use different orders).
– Matthieu M.
Jun 5 at 15:01
+1 for the array expansion trick. It's an important idiom to get used to when working with variadics, as it simplifies code quite a bit compared to recursion. The important point to remember is that the order of function calls is guaranteed here, which allows scheduling functions with side-effects; it would not be the case with a regular function call as the order of evaluation of function arguments is not guaranteed (and typically MSVC and gcc use different orders).
– Matthieu M.
Jun 5 at 15:01
|
show 4 more comments
If you can change your _inner_print function to a class, you can make use of a partial specialization to end the recursion:
template <std::size_t N, std::size_t MAX>
struct _inner_print
_inner_print()
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
;
template <std::size_t MAX> struct _inner_print<0, MAX> ;
Rather than calling _inner_print() as a function, it becomes a declaration for an unnamed temporary, invoking the constructor that performs your output.
perhapsconstexpr _inner_print()
– Swift - Friday Pie
Jun 5 at 4:38
add a comment |
If you can change your _inner_print function to a class, you can make use of a partial specialization to end the recursion:
template <std::size_t N, std::size_t MAX>
struct _inner_print
_inner_print()
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
;
template <std::size_t MAX> struct _inner_print<0, MAX> ;
Rather than calling _inner_print() as a function, it becomes a declaration for an unnamed temporary, invoking the constructor that performs your output.
perhapsconstexpr _inner_print()
– Swift - Friday Pie
Jun 5 at 4:38
add a comment |
If you can change your _inner_print function to a class, you can make use of a partial specialization to end the recursion:
template <std::size_t N, std::size_t MAX>
struct _inner_print
_inner_print()
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
;
template <std::size_t MAX> struct _inner_print<0, MAX> ;
Rather than calling _inner_print() as a function, it becomes a declaration for an unnamed temporary, invoking the constructor that performs your output.
If you can change your _inner_print function to a class, you can make use of a partial specialization to end the recursion:
template <std::size_t N, std::size_t MAX>
struct _inner_print
_inner_print()
std::cout << "Call #"<<MAX-N<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<N-1, MAX>();
;
template <std::size_t MAX> struct _inner_print<0, MAX> ;
Rather than calling _inner_print() as a function, it becomes a declaration for an unnamed temporary, invoking the constructor that performs your output.
edited Jun 5 at 18:02
answered Jun 5 at 4:27
1201ProgramAlarm1201ProgramAlarm
20.2k72741
20.2k72741
perhapsconstexpr _inner_print()
– Swift - Friday Pie
Jun 5 at 4:38
add a comment |
perhapsconstexpr _inner_print()
– Swift - Friday Pie
Jun 5 at 4:38
perhaps
constexpr _inner_print()– Swift - Friday Pie
Jun 5 at 4:38
perhaps
constexpr _inner_print()– Swift - Friday Pie
Jun 5 at 4:38
add a comment |
You should use partial specialization. But you can't do this with a function.
You should use a struct to do the trick.
#include <iostream>
#include <string>
#include <tuple>
template <std::size_t N, std::size_t MAX, class T>
struct inner_print_impl
static void run(const T& caller)
std::cout << "Call #"<<MAX-N<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
inner_print_impl<N-1, MAX , T>::run(caller);
;
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>
static void run(const T& caller)
std::cout << "Call #"<<MAX<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
// no recursion
;
template <typename ...Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t N_ = std::tuple_size<TypeList>::value;
template <std::size_t N, std::size_t MAX, class T>
friend struct inner_print_impl;
void print_this()
inner_print_impl<N_,N_, temp<Ts...> >::run(*this);
TypeList _mem;
private :
int a ; // test acces
;
int main()
std::string name;
temp<int, int, int> t1;
t1.print_this();
Note :
- If you need to acces to a private membre, you will need to pass
*thisto the call and add the newstructas friend of your class - In my code, I have a duplication on the
/* other dynamic code */part. You may :- Use a function
- make your template parameter a
intinstead ofsize_tand stop at-1rather than0
PS :
I don't get the part
in a tuple in a function in a struct (not in a function in the struct)
I hope I didn't miss something
Edit :
My code do one more iteration than your, you may just empty this :
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>
static void run(const T& caller)
;
And you don't display for the 0 case.
add a comment |
You should use partial specialization. But you can't do this with a function.
You should use a struct to do the trick.
#include <iostream>
#include <string>
#include <tuple>
template <std::size_t N, std::size_t MAX, class T>
struct inner_print_impl
static void run(const T& caller)
std::cout << "Call #"<<MAX-N<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
inner_print_impl<N-1, MAX , T>::run(caller);
;
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>
static void run(const T& caller)
std::cout << "Call #"<<MAX<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
// no recursion
;
template <typename ...Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t N_ = std::tuple_size<TypeList>::value;
template <std::size_t N, std::size_t MAX, class T>
friend struct inner_print_impl;
void print_this()
inner_print_impl<N_,N_, temp<Ts...> >::run(*this);
TypeList _mem;
private :
int a ; // test acces
;
int main()
std::string name;
temp<int, int, int> t1;
t1.print_this();
Note :
- If you need to acces to a private membre, you will need to pass
*thisto the call and add the newstructas friend of your class - In my code, I have a duplication on the
/* other dynamic code */part. You may :- Use a function
- make your template parameter a
intinstead ofsize_tand stop at-1rather than0
PS :
I don't get the part
in a tuple in a function in a struct (not in a function in the struct)
I hope I didn't miss something
Edit :
My code do one more iteration than your, you may just empty this :
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>
static void run(const T& caller)
;
And you don't display for the 0 case.
add a comment |
You should use partial specialization. But you can't do this with a function.
You should use a struct to do the trick.
#include <iostream>
#include <string>
#include <tuple>
template <std::size_t N, std::size_t MAX, class T>
struct inner_print_impl
static void run(const T& caller)
std::cout << "Call #"<<MAX-N<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
inner_print_impl<N-1, MAX , T>::run(caller);
;
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>
static void run(const T& caller)
std::cout << "Call #"<<MAX<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
// no recursion
;
template <typename ...Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t N_ = std::tuple_size<TypeList>::value;
template <std::size_t N, std::size_t MAX, class T>
friend struct inner_print_impl;
void print_this()
inner_print_impl<N_,N_, temp<Ts...> >::run(*this);
TypeList _mem;
private :
int a ; // test acces
;
int main()
std::string name;
temp<int, int, int> t1;
t1.print_this();
Note :
- If you need to acces to a private membre, you will need to pass
*thisto the call and add the newstructas friend of your class - In my code, I have a duplication on the
/* other dynamic code */part. You may :- Use a function
- make your template parameter a
intinstead ofsize_tand stop at-1rather than0
PS :
I don't get the part
in a tuple in a function in a struct (not in a function in the struct)
I hope I didn't miss something
Edit :
My code do one more iteration than your, you may just empty this :
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>
static void run(const T& caller)
;
And you don't display for the 0 case.
You should use partial specialization. But you can't do this with a function.
You should use a struct to do the trick.
#include <iostream>
#include <string>
#include <tuple>
template <std::size_t N, std::size_t MAX, class T>
struct inner_print_impl
static void run(const T& caller)
std::cout << "Call #"<<MAX-N<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
inner_print_impl<N-1, MAX , T>::run(caller);
;
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>
static void run(const T& caller)
std::cout << "Call #"<<MAX<< " " << caller.a << std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
// no recursion
;
template <typename ...Ts>
struct temp
using TypeList = std::tuple<Ts...>;
constexpr static std::size_t N_ = std::tuple_size<TypeList>::value;
template <std::size_t N, std::size_t MAX, class T>
friend struct inner_print_impl;
void print_this()
inner_print_impl<N_,N_, temp<Ts...> >::run(*this);
TypeList _mem;
private :
int a ; // test acces
;
int main()
std::string name;
temp<int, int, int> t1;
t1.print_this();
Note :
- If you need to acces to a private membre, you will need to pass
*thisto the call and add the newstructas friend of your class - In my code, I have a duplication on the
/* other dynamic code */part. You may :- Use a function
- make your template parameter a
intinstead ofsize_tand stop at-1rather than0
PS :
I don't get the part
in a tuple in a function in a struct (not in a function in the struct)
I hope I didn't miss something
Edit :
My code do one more iteration than your, you may just empty this :
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX , T>
static void run(const T& caller)
;
And you don't display for the 0 case.
edited Jun 5 at 14:47
answered Jun 5 at 4:39
Martin MorterolMartin Morterol
91428
91428
add a comment |
add a comment |
template<class Integral, Integral N>
auto dispatch( std::integral_constant<Integral, N> )
return [](auto&&...args)
return std::get<N>( std::forward_as_tuple( decltype(args)(args)... ) );
;
template<std::size_t N>
auto dispatch()
return dispatch( std::integral_constant<std::size_t, N> );
this little beauty gives you much of the magic of if constexpr in c++14.
template <std::size_t N, std::size_t MAX>
void _inner_print()
dispatch( std::integral_constant<bool, N==0> )
(
// 0, aka false branch:
[&](auto Nval)
std::cout << "Call #"<<MAX-Nval<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<Nval-1, MAX>();
,
// 1, aka true branch:
[](auto&&)
)( std::integral_constant<std::size_t, N> );
Live example.
Note that we have to pass in the N via an argument to the lambda, because we want the validity of the lambda's body to vary based on its argument, which is only passed to the right one.
Alternatively, you can use an overload helper:
template<class T0, class...Ts>
struct overloaded;
template<class Lhs, class Rhs, class...Ts>
struct overloaded<Lhs, Rhs, Ts...>:
overloaded<Lhs>, overloaded<Rhs,Ts...>
using overloaded<Lhs>::operator();
using overloaded<Rhs,Ts...>::operator();
template<class A0, class...As>
explicit overloaded(A0&&a0, As&&...as) :
overloaded<Lhs>(std::forward<A0>(a0)),
overloaded<Rhs,Ts...>( std::forward<As>(as)... )
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class T0>
struct overloaded<T0> : T0
using T0::operator();
template<class A0>
explicit overloaded(A0&&a0):
T0(std::forward<A0>(a0))
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class R, class...Args>
struct overloaded<R(*)(Args...)>
R operator()(Args... args) const
return pf(std::forward<Args>(args)...);
R(*pf)(Args...);
overloaded( R(*f)(Args...) ):pf(f)
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class...Args>
auto overload( Args&&...args )
return overloaded<std::decay_t<Args>...>(
std::forward<Args>(args)...
);
Here is how you can use overloaded:
template <std::size_t N, std::size_t MAX>
void _inner_print()
std::integral_constant<std::size_t, N> index;
overload(
[](std::integral_constant<std::size_t, 0>) ,
[&](auto index)
std::cout << "Call #"<<MAX-index<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<index-1, MAX>();
)(index);
note that the rule is that the only-sometimes-compiling dynamic code must depend on the auto arguments to the lambda.
Live example.
add a comment |
template<class Integral, Integral N>
auto dispatch( std::integral_constant<Integral, N> )
return [](auto&&...args)
return std::get<N>( std::forward_as_tuple( decltype(args)(args)... ) );
;
template<std::size_t N>
auto dispatch()
return dispatch( std::integral_constant<std::size_t, N> );
this little beauty gives you much of the magic of if constexpr in c++14.
template <std::size_t N, std::size_t MAX>
void _inner_print()
dispatch( std::integral_constant<bool, N==0> )
(
// 0, aka false branch:
[&](auto Nval)
std::cout << "Call #"<<MAX-Nval<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<Nval-1, MAX>();
,
// 1, aka true branch:
[](auto&&)
)( std::integral_constant<std::size_t, N> );
Live example.
Note that we have to pass in the N via an argument to the lambda, because we want the validity of the lambda's body to vary based on its argument, which is only passed to the right one.
Alternatively, you can use an overload helper:
template<class T0, class...Ts>
struct overloaded;
template<class Lhs, class Rhs, class...Ts>
struct overloaded<Lhs, Rhs, Ts...>:
overloaded<Lhs>, overloaded<Rhs,Ts...>
using overloaded<Lhs>::operator();
using overloaded<Rhs,Ts...>::operator();
template<class A0, class...As>
explicit overloaded(A0&&a0, As&&...as) :
overloaded<Lhs>(std::forward<A0>(a0)),
overloaded<Rhs,Ts...>( std::forward<As>(as)... )
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class T0>
struct overloaded<T0> : T0
using T0::operator();
template<class A0>
explicit overloaded(A0&&a0):
T0(std::forward<A0>(a0))
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class R, class...Args>
struct overloaded<R(*)(Args...)>
R operator()(Args... args) const
return pf(std::forward<Args>(args)...);
R(*pf)(Args...);
overloaded( R(*f)(Args...) ):pf(f)
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class...Args>
auto overload( Args&&...args )
return overloaded<std::decay_t<Args>...>(
std::forward<Args>(args)...
);
Here is how you can use overloaded:
template <std::size_t N, std::size_t MAX>
void _inner_print()
std::integral_constant<std::size_t, N> index;
overload(
[](std::integral_constant<std::size_t, 0>) ,
[&](auto index)
std::cout << "Call #"<<MAX-index<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<index-1, MAX>();
)(index);
note that the rule is that the only-sometimes-compiling dynamic code must depend on the auto arguments to the lambda.
Live example.
add a comment |
template<class Integral, Integral N>
auto dispatch( std::integral_constant<Integral, N> )
return [](auto&&...args)
return std::get<N>( std::forward_as_tuple( decltype(args)(args)... ) );
;
template<std::size_t N>
auto dispatch()
return dispatch( std::integral_constant<std::size_t, N> );
this little beauty gives you much of the magic of if constexpr in c++14.
template <std::size_t N, std::size_t MAX>
void _inner_print()
dispatch( std::integral_constant<bool, N==0> )
(
// 0, aka false branch:
[&](auto Nval)
std::cout << "Call #"<<MAX-Nval<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<Nval-1, MAX>();
,
// 1, aka true branch:
[](auto&&)
)( std::integral_constant<std::size_t, N> );
Live example.
Note that we have to pass in the N via an argument to the lambda, because we want the validity of the lambda's body to vary based on its argument, which is only passed to the right one.
Alternatively, you can use an overload helper:
template<class T0, class...Ts>
struct overloaded;
template<class Lhs, class Rhs, class...Ts>
struct overloaded<Lhs, Rhs, Ts...>:
overloaded<Lhs>, overloaded<Rhs,Ts...>
using overloaded<Lhs>::operator();
using overloaded<Rhs,Ts...>::operator();
template<class A0, class...As>
explicit overloaded(A0&&a0, As&&...as) :
overloaded<Lhs>(std::forward<A0>(a0)),
overloaded<Rhs,Ts...>( std::forward<As>(as)... )
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class T0>
struct overloaded<T0> : T0
using T0::operator();
template<class A0>
explicit overloaded(A0&&a0):
T0(std::forward<A0>(a0))
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class R, class...Args>
struct overloaded<R(*)(Args...)>
R operator()(Args... args) const
return pf(std::forward<Args>(args)...);
R(*pf)(Args...);
overloaded( R(*f)(Args...) ):pf(f)
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class...Args>
auto overload( Args&&...args )
return overloaded<std::decay_t<Args>...>(
std::forward<Args>(args)...
);
Here is how you can use overloaded:
template <std::size_t N, std::size_t MAX>
void _inner_print()
std::integral_constant<std::size_t, N> index;
overload(
[](std::integral_constant<std::size_t, 0>) ,
[&](auto index)
std::cout << "Call #"<<MAX-index<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<index-1, MAX>();
)(index);
note that the rule is that the only-sometimes-compiling dynamic code must depend on the auto arguments to the lambda.
Live example.
template<class Integral, Integral N>
auto dispatch( std::integral_constant<Integral, N> )
return [](auto&&...args)
return std::get<N>( std::forward_as_tuple( decltype(args)(args)... ) );
;
template<std::size_t N>
auto dispatch()
return dispatch( std::integral_constant<std::size_t, N> );
this little beauty gives you much of the magic of if constexpr in c++14.
template <std::size_t N, std::size_t MAX>
void _inner_print()
dispatch( std::integral_constant<bool, N==0> )
(
// 0, aka false branch:
[&](auto Nval)
std::cout << "Call #"<<MAX-Nval<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<Nval-1, MAX>();
,
// 1, aka true branch:
[](auto&&)
)( std::integral_constant<std::size_t, N> );
Live example.
Note that we have to pass in the N via an argument to the lambda, because we want the validity of the lambda's body to vary based on its argument, which is only passed to the right one.
Alternatively, you can use an overload helper:
template<class T0, class...Ts>
struct overloaded;
template<class Lhs, class Rhs, class...Ts>
struct overloaded<Lhs, Rhs, Ts...>:
overloaded<Lhs>, overloaded<Rhs,Ts...>
using overloaded<Lhs>::operator();
using overloaded<Rhs,Ts...>::operator();
template<class A0, class...As>
explicit overloaded(A0&&a0, As&&...as) :
overloaded<Lhs>(std::forward<A0>(a0)),
overloaded<Rhs,Ts...>( std::forward<As>(as)... )
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class T0>
struct overloaded<T0> : T0
using T0::operator();
template<class A0>
explicit overloaded(A0&&a0):
T0(std::forward<A0>(a0))
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class R, class...Args>
struct overloaded<R(*)(Args...)>
R operator()(Args... args) const
return pf(std::forward<Args>(args)...);
R(*pf)(Args...);
overloaded( R(*f)(Args...) ):pf(f)
overloaded(overloaded const&)=default;
overloaded(overloaded &&)=default;
overloaded& operator=(overloaded const&)=default;
overloaded& operator=(overloaded &&)=default;
;
template<class...Args>
auto overload( Args&&...args )
return overloaded<std::decay_t<Args>...>(
std::forward<Args>(args)...
);
Here is how you can use overloaded:
template <std::size_t N, std::size_t MAX>
void _inner_print()
std::integral_constant<std::size_t, N> index;
overload(
[](std::integral_constant<std::size_t, 0>) ,
[&](auto index)
std::cout << "Call #"<<MAX-index<<std::endl;
////////////////////////
/* other dynamic code */
////////////////////////
_inner_print<index-1, MAX>();
)(index);
note that the rule is that the only-sometimes-compiling dynamic code must depend on the auto arguments to the lambda.
Live example.
answered Jun 5 at 14:25
Yakk - Adam NevraumontYakk - Adam Nevraumont
193k21210399
193k21210399
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56454091%2fif-constexpr-with-recursive-parameter-packs%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Something new to learn for me!
– RC0993
Jun 5 at 4:24
constexpr static std::size_t _N = sizeof...(Ts);is a simpler way to achieve the same... Be aware, though, that identifers starting with underscore followed by capital letter are reserved (as well as those containing two underscores).– Aconcagua
Jun 5 at 5:03