I would like to use templates for optimization as described here. However, with a growing number of bool template arguments, instantiating the template might have too many branches. And it gets even more branchy if you use larger enums instead of bools.
#include <iostream>
using namespace std;
template <bool b1, bool b2>
int HeavyLoop_impl(int arg)
{
for (int i = 0; i < 10000000; i++)
{
// b1 is known at compile-time, so this branch will be eliminated
if (b1) { arg += 1; }
else { arg += 2; }
// b2 is known at compile-time, so this branch will be eliminated
if (b2) { arg += 10; }
else { arg += 20; }
}
return arg;
}
// This function could be generated automatically
void HeavyLoop(bool b1, bool b2, int arg)
{
int res;
if (b1) {
if (b2) { res = HeavyLoop_impl<true, true>(arg); }
else { res = HeavyLoop_impl<true, false>(arg); }
} else {
if (b2) { res = HeavyLoop_impl<false, true>(arg); }
else { res = HeavyLoop_impl<false, false>(arg); }
}
cout << "res: "<<res<<endl;
}
int main(int argc, char**argv)
{
bool b1 = true;
bool b2 = false;
int arg = 0;
HeavyLoop(b1, b2, arg);
return 0;
}
Is there any way to automatically generate the HeavyLoop function? I would like something like this:
vars_to_template_function<bool, bool>(HeavyLoop_impl, b1, b2, arg);
Would that be possible somehow? Thanks for any hints.
Note: this is only a very simplified example. The actual loop is is of course more complicated :o)
I decided to have some more fun with the code, here's an improved version over my first attempt which has the following benefits:
Supports enum types
Explicitly specify how many parameters should be converted
Generic implementation for the complicated part, one small helper for each function that uses it.
The code:
#include <iostream>
#include <utility>
#include <type_traits>
// an enum we would like to support
enum class tribool { FALSE, TRUE, FILE_NOT_FOUND };
// declare basic generic template
// (independent of a specific function you'd like to call)
template< template< class > class CB, std::size_t N, typename = std::tuple<> >
struct var_to_template;
// register types that should be supported
template< template< class > class CB, std::size_t N, typename... Cs >
struct var_to_template< CB, N, std::tuple< Cs... > >
{
// bool is pretty simple, there are only two values
template< typename R, typename... Args >
static R impl( bool b, Args&&... args )
{
return b
? var_to_template< CB, N-1, std::tuple< Cs..., std::true_type > >::template impl< R >( std::forward< Args >( args )... )
: var_to_template< CB, N-1, std::tuple< Cs..., std::false_type > >::template impl< R >( std::forward< Args >( args )... );
}
// for each enum, you need to register all its values
template< typename R, typename... Args >
static R impl( tribool tb, Args&&... args )
{
switch( tb ) {
case tribool::FALSE:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::FALSE > > >::template impl< R >( std::forward< Args >( args )... );
case tribool::TRUE:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::TRUE > > >::template impl< R >( std::forward< Args >( args )... );
case tribool::FILE_NOT_FOUND:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::FILE_NOT_FOUND > > >::template impl< R >( std::forward< Args >( args )... );
}
throw "unreachable";
}
// in theory you could also add int, long, ... but
// you'd have to switch on every possible value that you want to support!
};
// terminate the recursion
template< template< class > class CB, typename... Cs >
struct var_to_template< CB, 0, std::tuple< Cs... > >
{
template< typename R, typename... Args >
static R impl( Args&&... args )
{
return CB< std::tuple< Cs... > >::template impl< R >( std::forward< Args >( args )... );
}
};
// here's your function with the template parameters
template< bool B, tribool TB >
int HeavyLoop_impl( int arg )
{
for( int i = 0; i < 10000000; i++ ) {
arg += B ? 1 : 2;
arg += ( TB == tribool::TRUE ) ? 10 : ( TB == tribool::FALSE ) ? 20 : 30;
}
return arg;
}
// a helper class, required once per function that you'd like to forward
template< typename > struct HeavyLoop_callback;
template< typename... Cs >
struct HeavyLoop_callback< std::tuple< Cs... > >
{
template< typename R, typename... Args >
static R impl( Args&&... args )
{
return HeavyLoop_impl< Cs::value... >( std::forward< Args >( args )... );
}
};
// and here, everything comes together:
int HeavyLoop( bool b, tribool tb, int arg )
{
// you provide the helper and the number of arguments
// that should be converted to var_to_template<>
// and you provide the return type to impl<>
return var_to_template< HeavyLoop_callback, 2 >::impl< int >( b, tb, arg );
}
int main()
{
bool b = true;
tribool tb = tribool::FALSE;
int arg = 0;
int res = HeavyLoop( b, tb, arg );
std::cout << "res: " << res << std::endl;
return 0;
}
And here's a live example in case you want to play with it.
Here's how you can do it:
#include <iostream>
using namespace std;
template <bool b1, bool b2>
struct HeavyLoopImpl
{
static int func(int arg)
{
for (int i = 0; i < 10000000; i++) {
arg += b1 ? 1 : 2;
arg += b2 ? 10 : 20;
}
return arg;
}
};
template <template<bool...> class Impl,bool...Bs>
struct GenericJump
{
template<typename... Args>
static int impl(Args&&... args)
{
return Impl<Bs...>::func(std::forward<Args>(args)...);
}
template<typename... Args>
static int impl(bool b, Args&&... args)
{
return b
? GenericJump<Impl,Bs...,true >::impl(std::forward<Args>(args)...)
: GenericJump<Impl,Bs...,false>::impl(std::forward<Args>(args)...);
}
};
int HeavyLoop(bool b1, bool b2, int arg)
{
return GenericJump<HeavyLoopImpl>::impl(b1,b2,arg);
}
int main()
{
bool b1 = true;
bool b2 = false;
int arg = 0;
int res = HeavyLoop(b1, b2, arg);
cout << "res: "<<res<<endl;
return 0;
}
This is basically Daniels solution, but it allows you to use functions other than HeavyLoop_impl() as implementation. Only being able to call a single template function kind of defeats the purpose of being a generic solution. The GenericJump template class can call other functions also. You only have to change the HeavyLoop_impl() template function into a template class with a static function func(). It works marvellously. It compiles with gcc 4.7.3 and gives the correct output.
The ideal generic solution really depends on what you want to vary. The possibilities for variation are:
The number of branches, and the types of the variables being branched on.
The operation to be performed and the number and types of its arguments.
I would recommend not making an completely generic solution unless you really need to vary all of those things. Consider only the things you want to vary and your life will be much easier.
Assuming that the total number of branches is less than 2^64, you can use a switch statement to do the dispatch. The following solution demonstrates how this could work:
template<unsigned permutation>
struct Permutation
{
static_assert(permutation < 4, "permutation must be in the range [0, 4)");
static const bool b1 = permutation & (1 << 0);
static const bool b2 = permutation & (1 << 1);
};
unsigned makePermutation(bool b1, bool b2)
{
return (b1 << 0) | (b2 << 1);
}
template<unsigned p>
int HeavyLoop_impl(int arg)
{
return HeavyLoop_impl<Permutation<p>::b1, Permutation<p>::b2>(arg);
}
int HeavyLoop_impl(unsigned permutation, int arg)
{
switch(permutation)
{
case 0: return HeavyLoop_impl<0>(arg);
case 1: return HeavyLoop_impl<1>(arg);
case 2: return HeavyLoop_impl<2>(arg);
case 3: return HeavyLoop_impl<3>(arg);
}
}
[Note: It would be trivial to use Boost.Preprocessor to generate the above switch statement.]
void HeavyLoop(bool b1, bool b2, int arg)
{
int res = HeavyLoop_impl(makePermutation(b1, b2), arg);
cout << "res: "<<res<<endl;
}
I think the best answer to your question is actually not to generate it automatically and leave it how you already have it in the question.
Making an automatic template function to generate the middle ground just obfuscates the invariant switching you're doing in the first place.
I much prefer to try to understand how the middle layer works in your question, than any of the answers people have offered for you.
I have a similar example. In my case I can apply a number of different operations between an array of values. The arrays are equal size. However I also have a structure that maps sub-ranges of the array with weight values that affect my operations.
So for instance, I might be working with arrays of 100 values, and have ranges with weights like this:
[0,25] rangeWeight = 0
[26,35] rangeWeight = 0.25
[36,50] rangeWeight = 0.5
[51,99] rangeWeight = 1.0
So each operation looks something like (pseudo):
for each subrange:
alias to the dst buffer
alias to the src buffer
determine the number of elements in the range
if there's any
weight = weightPassedIn * rangeWeight;
Op(dst, src, weight, numElements);
For me, there were several optimizations involving whether or not the destination is touched or not (if it was still at the cleared value, some assumptions can be made to simplify the math per operation), Also if the weight happens to be full, 1.0, there are other shortcuts.
At first I was writing the same loop over and over with all of the same setup, and once I refactored all the loops around each op into functions, I pretty much naturally had the form of your invariant wrapper. There actually turned out to be several wrappers which mainly just wrap the high level operation happening inside the loop, and otherwise just handle the individual optimizations like this:
if (weight == 1.0f)
{
if ( arrayIsCleared )
Blend<BlendOpSet, true, false>(otherBuff, subRangesMask, 1.0f);
else
Blend<BlendOpAccumulate, true, false>(otherBuff, subRangesMask, 1.0f);
}
else
{
if ( arrayIsCleared )
Blend<BlendOpSet, false, false>(otherBuff, subRangesMask, weight);
else
Blend<BlendOpAccumulate, false, false>(otherBuff, subRangesMask, weight);
}
Have you considered passing a function as template argument? This way you can tailor your inner loop to your own wishes.
Function passed as template argument
However, the function call will have a small overhead.
Here is another solution with boost::hana, which can handle also enums:
#include <cstdio>
#include <type_traits>
#include <boost/hana.hpp>
namespace hana = boost::hana;
template <typename F, typename TArgs, typename TIn, typename TOut>
void fun_arg_combinations_impl(F&& f, TArgs targs, TIn tin, TOut tout) {
if constexpr (hana::is_empty(tin)) {
hana::unpack(tout, f);
} else {
hana::for_each(hana::front(tin), [&](auto v){
if (v == hana::front(targs)) {
fun_arg_combinations_impl(f, hana::drop_front(targs), hana::drop_front(tin), hana::append(tout, v));
}
});
}
}
template <typename F, typename TArgs, typename TIn>
void fun_arg_combinations(F&& f, TArgs targs, TIn tin) {
fun_arg_combinations_impl(f, targs, tin, hana::tuple<>());
}
enum Shape {LINE, CIRCLE, SQUARE};
int main()
{
auto f_heavy_loop = [](auto b1t, auto b2t, auto st) {
constexpr bool b1 = decltype(b1t)::value;
constexpr bool b2 = decltype(b2t)::value;
constexpr Shape s = decltype(st )::value;
printf("out:%d %d %d\n", b1, b2, s);
};
//constexpr auto bools = hana::make_tuple(std::true_type{}, std::false_type{});
constexpr auto bools = hana::tuple<std::true_type, std::false_type>{};
constexpr auto shapes = hana::tuple<
std::integral_constant<Shape, LINE>,
std::integral_constant<Shape, CIRCLE>,
std::integral_constant<Shape, SQUARE>>{};
// Using volatile to not allow the compiler to optimize for hard-coded values
volatile bool b1 = true;
volatile bool b2 = false;
volatile Shape s = SQUARE;
fun_arg_combinations(
f_heavy_loop,
hana::make_tuple(b1 , b2 , s ),
hana::make_tuple(bools, bools, shapes));
}
b1, b2 and s inside the f_heavy_loop() lambda are all constexpr, so we can use if constexpr on them.
output:
out:1 0 2
Have a look at the generated assembly here: https://godbolt.org/z/nsF2l5
Related
I wanted to write a generic sum function like the following one but not in template syntax but in lambda syntax:
template<typename T>
auto Sum(T lastSummand)
{
return lastSummand;
}
template<typename T, typename... Ts>
auto Sum(T firstSummand, Ts... restSummands)
{
return firstSummand + Sum(restSummands...);
}
Because generic lambdas are mapped to templates it should be possible to do something like:
auto sum = [](auto firstSummand, auto... restSummands) { ... };
But I cannot figure out how to do the recursion using lambdas. Searching in this and other locations did not bring forward much.
In C++14 you don't actually need recursion to do that with generic lambdas.
As an example, you can do this:
#include<type_traits>
#include<iostream>
int main() {
auto l = [](auto... values) {
std::common_type_t<decltype(values)...> ret = {};
decltype(ret) _[] = { (ret += values)... };
(void)_;
return ret;
};
auto v = l(0, 0., 5, 4.2);
std::cout << v << std::endl;
}
Return type is given by the std::common_type_t of the given pack.
The rest of the code contains the common pattern usually used while waiting for fold expressions.
In C++17 it will become:
#include<iostream>
int main() {
auto l = [](auto... values) { return (values + ...); };
auto v = l(0, 0., 5, 4.2);
std::cout << v << std::endl;
}
See it on wandbox.
If you want to verify on the fly that given parameters are all of arithmetic types, you can use the bool trick as it follows:
auto l = [](auto... values) {
static_assert(
std::is_same<
std::integer_sequence<bool, true, std::is_arithmetic<decltype(values)>::value...>,
std::integer_sequence<bool, std::is_arithmetic<decltype(values)>::value..., true>
>::value, "!"
);
std::common_type_t<decltype(values)...> ret = {};
decltype(ret) _[] = { (ret += values)... };
(void)_;
return ret;
};
Well, i'm assuming you need a functor-like type to calculate the sum if you're using the lambda. If that's the case then I suppose you could write a small generic class to take the place of that lambda.
template < typename T > struct Sum
{
template < typename U >
T operator () (U v) const noexcept
{
return static_cast< T >(v);
}
template < typename U, typename... Values >
T operator () (U v, Values&&... vs) const noexcept
{
return static_cast< T >(v) + (*this)(std::forward< Values >(vs)...);
}
};
And used as:
auto sum = Sum< int >();
printf("%d\n", sum(23, 4.0, true, 7));
I wrote it in such way that the return type can be specified in advance. But I suppose you could adjust it to make that generic as well.
If this was not your intention then please ignore this answer.
I would like to be able to do something like the following:
struct A {};
template<typename T, typename U> struct B : public A {};
std::unique_ptr<A> chooseB(int i, int j)
{
// return B<T, U> where T and U vary with i and j (0 = int, 1 = double etc.)
}
i and j are not known at compile time. Potentially the list of types could be long enough that just forming a big conditional that enumerates all possible pairs of i and j could be painful.
Is there are nice way to write the chooseB function? I might potentially want to generalize this to more than two template parameters.
Time for some abuse.
We're going to write a function that will figure out which type corresponds to a given index. But we can't return that type - the return type must be known at compile-time. So instead we'll forward that along to a function that we provide. I'll start with the end-result:
std::unique_ptr<A> chooseB(int i, int j)
{
using choices = typelist<int, double>;
return pick_elem<std::unique_ptr<A>>(i, choices{},
[=](auto tag1){
return pick_elem<std::unique_ptr<A>>(j, choices{},
[=](auto tag2) {
using T1 = typename decltype(tag1)::type;
using T2 = typename decltype(tag2)::type;
return std::make_unique<B<T1, T2>>();
});
});
}
pick_elem takes three arguments: the index, the list of choices, and the function to forward along with. So we call it with the first index and call a lambda -- which in turns calls it with the second index and calls another lambda -- which finally makes our B.
You have to explicitly provide the return type to pick_elem<> because it needs to halt recursion somehow and that end-step will need to know what to return. So we start with some convenient metaprogramming helpers:
template <class... > struct typelist {};
template <class T> struct tag { using type = T; };
And then pick_elem is:
template <class R, class F>
R pick_elem(int , typelist<>, F )
{
throw std::runtime_error("wtf");
}
template <class R, class T, class... Ts, class F>
R pick_elem(int i, typelist<T, Ts...>, F f)
{
if (i == 0) {
return f(tag<T>{});
}
else {
return pick_elem<R>(i-1, typelist<Ts...>{}, f);
}
}
This can be generalized to arbitrarily many types by making chooseB variadic itself. In a way, this is actually cleaner than what I had before. chooseB() now additionally takes some non-deducible arguments that are the types we've figured out so far:
template <class... Ts>
std::unique_ptr<A> chooseB()
{
return std::make_unique<B<Ts...>>();
}
template <class... Ts, class Idx, class... Rest>
std::unique_ptr<A> chooseB(Idx i, Rest... rest)
{
using choices = typelist<int, double>;
return pick_elem<std::unique_ptr<A>>(i, choices{},
[=](auto tag){
return chooseB<Ts..., typename decltype(tag)::type>(rest...);
});
}
You can add some safety around this by asserting that the number of arguments is correct.
My pragmatic solution inspired by 0x499602D2 above. This will actually probably work for OK for the problem at hand - but I was interested in more general answers that don't rely on specific enumeration of all possibilities.
struct A
{
virtual ~A() = default;
virtual void printTypes() = 0;
};
template<typename T, typename U>
struct B : public A
{
virtual void printTypes() override
{
std::cout << typeid(T).name() << ", " << typeid(U).name() << std::endl;
}
};
int main()
{
std::map<std::pair<int, int>, std::function<std::unique_ptr<A> ()>> m =
{
{{0, 0}, [](){return std::unique_ptr<A>(new B<int, int>);}},
{{0, 1}, [](){return std::unique_ptr<A>(new B<int, double>);}},
{{1, 0}, [](){return std::unique_ptr<A>(new B<double, int>);}},
{{1, 1}, [](){return std::unique_ptr<A>(new B<double, double>);}}
};
for (int i = 0; i < 2; ++i)
{
for (int j = 0; j < 2; ++j)
{
auto a = m[std::make_pair(i, j)]();
a->printTypes();
}
}
}
You can map both numbers to instantiations of the base class:
#define FWD(x) std::forward<decltype(x)>((x))
auto p=[](auto&&l,auto&&r){return std::make_pair(FWD(l),FWD(r));};
std::unique_ptr<A> chooseB(int i, int j) {
std::map<std::pair<int,int>,std::unique_ptr<A>> m;
m.emplace(p(p(0,0),std::make_unique<B<int,int>>()));
m.emplace(p(p(0,1),std::make_unique<B<int,double>>()));
m.emplace(p(p(1,0),std::make_unique<B<double,int>>()));
m.emplace(p(p(1,1),std::make_unique<B<double,double>>()));
/* and so on for different (i,j) */
return std::move(m[p(i,j)]);
}
If you want to generalize this for more than two parameters, then have the function take a parameter pack (Args&&...) possibly SFINAE-ified to all integers, and use a std::tuple<std::decay_t<Args...>> instead of a pair.
Make a jump table and dispatch.
template<size_t I, class... Ts>
std::unique_ptr<A> do_make() {
using T = std::tuple_element_t<I / sizeof...(Ts), std::tuple<Ts...>>;
using U = std::tuple_element_t<I % sizeof...(Ts), std::tuple<Ts...>>;
return std::make_unique<B<T,U>>();
}
template<class... Ts, size_t...Is>
std::unique_ptr<A> make(size_t i, size_t j, std::index_sequence<Is...>) {
using fptr_t = std::unique_ptr<A> (*)();
static constexpr fptr_t table[] = { do_make<Is, Ts...> ...};
return table[ i * sizeof...(Ts) + j]();
}
template<class... Ts>
std::unique_ptr<A> make(size_t i, size_t j) {
return make<Ts...>(i, j, std::make_index_sequence<sizeof...(Ts) * sizeof...(Ts)>());
}
Suppose I have a versatile function with about four boolean flags:
int do_something(int arg, bool flag1, bool flag2, bool flag3, bool flag4) {
for(int i = 0; i < 1000000; i++) {
if(flag1)
// Do something 1
if(flag2)
// Do something 2
if(flag3)
// Do something 3
if(flag4)
// Do something 4
//Do something else 5
}
}
But I don't want to incur any costs for branching on these flags in the inner loop so I change them to templates (allowing the compiler to optimize away the conditionals):
template<bool flag1, bool flag2, bool flag3, bool flag4>
int do_something_helper(int arg) {
for(int i = 0; i < 1000000; i++) {
if(flag1)
// Do something 1
if(flag2)
// Do something 2
if(flag3)
// Do something 3
if(flag4)
// Do something 4
//Do something else 5
}
}
How can I write the do_something method now? The only way I know is as follows:
int do_something(int arg, bool flag1, bool flag2, bool flag3, bool flag4) {
if(flag1) {
if(flag2) {
if(flag3) {
if(flag4) {
return do_something_helper<true,true,true,true>(arg);
}else{
return do_something_helper<true,true,true,false>(arg);
}
}else{
if(flag4) {
return do_something_helper<true,true,false,true>(arg);
}else{
return do_something_helper<true,true,false,false>(arg);
}
}
//... You get the picture
}
Is there some way to get the compiler to write the above code automatically so I don't have to include this ugly monstrosity in my beautiful code-base?
What I would do is take a functor and a pack of arguments and an argument index and a range. Then I would replace the indexed argument with std::integral_constant<type, value> and call the functor. The bool case is easiest, as the range is obvious, so I would write that one first.
Then you can chain such replaces and functors to replace each one of the bools with compile time types. I would use the same functor for them all, with N overloads, esch replacing one bool with std::integral_constant<bool, X> where X is a template parameter.
The last one would then call the final method, with integral_constant instead of bool.
Note that this expands to an exponential amount of instantiations, so be careful.
The argument manipulation code would be fun to write.
Here is a live example.
Amusingly, the boilerplate to do the above is probably still bulkier, but hopefully less typo-prone and easier to test.
#include <iostream>
#include <tuple>
template<unsigned...Is> struct indexes {typedef indexes<Is...> type;};
template<unsigned min, unsigned max, unsigned...Is> struct make_indexes: make_indexes<min, max-1, max-1, Is...> {};
template<unsigned min, unsigned...Is> struct make_indexes<min, min, Is...>: indexes<Is...> {};
template<unsigned max, unsigned min=0>
using Indexes = typename make_indexes<min, max>::type;
template<unsigned index, typename Functor, typename... Args, unsigned... Before, unsigned... After>
void map_bool_to_compile_time_helper( indexes<Before...>, indexes<After...>, Functor&& f, std::tuple<Args...> args )
{
if (std::get<index>( args )) {
std::forward<Functor>(f)( std::get<Before>(args)..., std::true_type(), std::get<After>(args)... );
} else {
std::forward<Functor>(f)( std::get<Before>(args)..., std::false_type(), std::get<After>(args)... );
}
}
template<unsigned index, typename Functor, typename... Args>
void map_bool_to_compile_time( Functor&& f, Args&&... args )
{
map_bool_to_compile_time_helper<index>( Indexes<index>(), Indexes<sizeof...(Args), index+1>(), std::forward<Functor>(f), std::make_tuple<Args&&...>(std::forward<Args>(args)...) );
}
template<typename Functor, unsigned... indexes>
struct map_bools_to_compile_time_helper;
template<typename Functor, unsigned index, unsigned... indexes>
struct map_bools_to_compile_time_helper<Functor, index, indexes...> {
Functor&& f;
map_bools_to_compile_time_helper(Functor&& in):f(std::forward<Functor>(in)) {}
template< typename... Args>
void operator()( Args&&... args) const {
map_bool_to_compile_time<index>( map_bools_to_compile_time_helper<Functor, indexes...>{std::forward<Functor>(f)}, std::forward<Args>(args)... );
}
};
template<typename Functor>
struct map_bools_to_compile_time_helper<Functor> {
Functor&& f;
map_bools_to_compile_time_helper(Functor&& in):f(std::forward<Functor>(in)) {}
template<typename... Args>
void operator()( Args&&... args) const {
std::forward<Functor>(f)(std::forward<Args>(args)...);
}
};
template<unsigned... Is, typename Functor, typename... Args>
void map_bools_to_compile_time( indexes<Is...>, Functor&& f, Args&&... args ) {
map_bools_to_compile_time_helper<Functor, Is...>{ std::forward<Functor>(f) }( std::forward<Args>(args)... );
}
struct test {
template<bool b>
void operator()( int x, std::integral_constant< bool, b > ) { std::cout << x << ": " << b <<"!\n"; }
};
struct test2 {
template<bool b0, bool b1, bool b2>
void operator()( int x, std::integral_constant< bool, b0 >, std::integral_constant< bool, b1 >, std::integral_constant< bool, b2 > )
{
std::cout << x << ": " << b0 << b1 << b2 << "\n";
}
};
int main() {
map_bools_to_compile_time( indexes<1>(), test(), 1, true );
map_bool_to_compile_time<1>( test(), 2, false );
map_bools_to_compile_time( indexes<1,2,3>(), test2(), 3, true, false, true );
}
Updated with support for any number of arguments at any number of indexes.
You can use templates to organize static dispatch - which will allow to replace branching statement with function overload. This is a rather simple idea, here is a small example:
template <int Val>
struct Int2Type
{
static const int val_= Val;
};
int do_something(int arg, Int2Type<1>)
{
// do smth when flag == 1
}
int do_something(int arg, Int2Type<2>)
{
// do smth when flag == 2
}
... the same principle is applied (by the value of a flag needed overloaded function is called)
Let's say that I have a parameter pack I'm unrolling, e.g.
template<typename... P> void f(P...&& args) {
some_other_func(std::forward<P>(args)...);
}
Now let's say that I have some other minor function that these objects need to go through.
template<typename T> T&& some_func(T&& ref) {
// replace with actual logic
return std::forward<T>(ref);
}
I would normally just replace with
template<typename... P> void f(P...&& args) {
some_other_func(some_func(args)...);
}
But what do I do if some_func requires more information about the parameter than just it's type, like for example, it's position numerically in the parameter pack? So that instead of expanding to
some_other_func(some_func(arg1), some_func(arg2));
I could mke it expand to
some_other_func(some_func(arg1, 1), some_func(arg2, 2));
for example?
I know I've solved this before but can't recall how. Oh well, here's a fresh look.
The sequence of numbers can be translated into the argument sequence using std::get, so it is more fundamental. So, assuming that I need to implement some kind of custom tool, a number pack generator seems like a good choice.
(Gah, this was incredibly tedious. I did peek at Howard's answer and learned about forward_as_tuple, but that function doesn't even exist yet on my compiler or ideone.com, so blah. There are a lot of things I still need to get straight, and this is certainly one of the worst functional languages ever invented.)
http://ideone.com/u5noV
#include <tuple>
// Generic pack array (metacontainer)
template< typename T, T ... seq > struct value_sequence {
// Append a value to the array (metafunction)
template< T val > struct append
{ typedef value_sequence< T, seq..., val > type; };
};
// Generate a sequential array (metafunction)
template< size_t N >
struct index_sequence {
typedef typename index_sequence< N - 1 >::type
::template append< N - 1 >::type type;
};
template<>
struct index_sequence< 0 >
{ typedef value_sequence< size_t > type; };
// Generate indexes up to size of given tuple (metafunction)
template< typename T >
struct index_tuple {
typedef typename index_sequence< std::tuple_size< T >::value
>::type type;
};
// The magic function: passes indexes, makes all the function calls
template< typename F, typename G,
typename T, size_t ... N >
void compose_with_indexes_helper( F f, G g, T args,
value_sequence< size_t, N ... > ) {
f( g( std::get< N >( args ), N ) ... );
}
template< typename F, typename G, typename ... T >
void compose_with_indexes( F f, G g, T && ... args ) {
typedef std::tuple< T && ... > tuple_t;
compose_with_indexes_helper
// forwarding seems broken on ideone.com/GCC 4.5.1, work around.
// ( f, g, std::forward_as_tuple( std::forward( args ) ... ) );
( f, g, tuple_t( args ... ), typename index_tuple< tuple_t >::type() );
}
It is a little convoluted. But here is a working prototype of your code using several private utilities of libc++, found in
<__tuple>, and <tuple>.
#include <iostream>
#include <tuple>
template<typename T>
int
some_func(T&& ref, size_t I)
{
std::cout << "ref = " << ref << ", I = " << I << '\n';
return 0;
}
template<typename... T, size_t ...Indx>
void
some_other_func(std::tuple<T...> ref, std::__tuple_indices<Indx...>) {
// replace with actual logic
std::__swallow(some_func(std::get<Indx>(ref), Indx)...);
}
template<typename... P>
void
f(P&&... args)
{
some_other_func(std::forward_as_tuple<P...>(std::forward<P>(args)...),
typename std::__make_tuple_indices<sizeof...(P)>::type());
}
int main()
{
f("zero", "one", "two", "three");
}
ref = zero, I = 0
ref = one, I = 1
ref = two, I = 2
ref = three, I = 3
Say I have a
struct SMyStruct
{
int MULT;
int VAL;
};
std::map<std::string, SMyStuct*> _idToMyStructMap;
Now I want to calculate total of all SMyStuct, where total is defined as MULT1 *VAL1 + MULT2 *VAL2 for each elements in the idToMyStructMap.
Seems like accumulate function is a natural choice. Please suggest. thanks
No Boost please.... just an 'ld fashion stl
typedef std::map< std::string, SMyStruct* > string_to_struct_t;
int add_to_totals( int total, const string_to_struct_t::value_type& data )
{
return total + data.second->MULT * data.second->VAL;
}
const int total = std::accumulate(
_idToMyStructMap.begin(),
_idToMyStructMap.end(),
0,
add_to_totals );
A variation on the theme would be to define operator+ for your struct, and then just use std::accumulate in its default mode.
int & operator+ (const int &lhs, const SMyStruct &rhs){
return lhs + (rhs.MULT * rhs.VALUE);
}
Then:
std::accumulate(_idToMyStructMap.begin(), _idToMyStructMap.end(), 0);
Of course, if operator+ makes sense in general for your struct, then you'd want to add overloads for using SMyStruct on the left as well, and/or make them templates so that you get functions for int, float, double, long, etc. all in one shot. As jalf mentioned in comments, if operator+ (or this version of it) doesn't make sense in general for your struct, then the other solution is better.
You can also separate the 'take second of pair' functionality from 'calculate MULT*VAL' and 'add something to an accumulator'.
Though you don't need boost to do this, they already created a great deal of a 'functional' programming framework. If you can't use boost, you need some template magic of your own. Not too complicated, though.
#include <map>
#include <algorithm>
#include <numeric>
#include <functional>
#include <iostream>
Now I deem it better to put the multiplication inside the class.
struct SMyStruct
{
int MULT;
int VAL;
long f() const { return MULT*VAL; }
};
Create a generic functor for 'take second of pair':
// a 'take-second' functor
template< typename at_pair >
struct to_second_t : public std::unary_function< at_pair, typename at_pair::second_type > {
const typename at_pair::second_type& operator()( const at_pair & p ) const {
return p.second;
}
};
This looks tricky, but is merely a generic way of saying: 'first do this, then do that with the result':
// compose two functors (simplified)
template< typename at_F, typename at_G >
struct compose_t : public std::unary_function< typename at_F::argument_type, typename at_G::result_type >{
at_F f;
at_G g;
compose_t( at_F& f, at_G& g ): f( f ), g(g) {}
typename at_G::result_type operator()( const typename at_F::argument_type& v ) const {
return g( f( v ) );
}
};
template< typename at_F, typename at_G >
compose_t<at_F, at_G> compose( at_F& f, at_G& g ) { return compose_t<at_F,at_G>( f, g ); }
// compose two functors (a unary one, and a binary one)
//
template< typename at_F, typename at_G >
struct compose2_t : public std::binary_function< typename at_F::first_argument_type, typename at_G::argument_type, typename at_G::result_type >{
at_F f;
at_G g;
compose2_t( at_F& f, at_G& g ): f( f ), g(g) {}
typename at_G::result_type operator()( const typename at_F::first_argument_type& a1, const typename at_G::argument_type& v ) const {
return f( a1, g( v ) );
}
};
template< typename at_F, typename at_G >
compose2_t<at_F, at_G> compose2( at_F& f, at_G& g ) { return compose2_t<at_F,at_G>( f, g ); }
And finally, putting it all in practice:
int main()
{
typedef std::map<int, SMyStruct > tMap;
tMap m;
SMyStruct s = {1,2};
m[1].VAL = 1; m[1].MULT = 3;
m[2].VAL = 2; m[2].MULT = 10;
m[3].VAL = 3; m[3].MULT = 2;
// mind, this is not LISP (yet)
long total = std::accumulate( m.begin(), m.end(), 0,
compose2(
std::plus<int>(),
compose(
to_second_t<tMap::value_type>(),
std::mem_fun_ref( &SMyStruct::f ) ) )
);
std::cout << "total: " << total <<std::endl;
return 0;
}