I've made a function that calculates the sine of a number. It returns the input type if it is std::is_floating_point. But for std::is_integral, it returns a double.
template<class T , typename std::enable_if<std::is_integral<T>::value>::type* = nullptr >
double mysin(const T& t) // note, function signature is unmodified
{
double a = t;
return std::sin(a);
}
template<class T , typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr >
T mysin(const T& t) // note, function signature is unmodified
{
return std::sin(t);
}
Easy enough. Now I'd like this to work for vectors (or arrays) and tuples (or clusters). So that:
(pseudo code:)
std::vector<std::double> a = mysin(std::vector<std::int>);
std::tuple<std::double, std::float> b = mysin(std::tuple<std::int, std::float>);
std::vector<std::tuple<std::double, std::float>> c = mysin(std::vector<std::tuple<std::int, std::float>>);
std::tuple<std::vector<std::double>, std::float> d = mysin(std::tuple<std::vector<std::int>, std::float>);
std::tuple<std::tuple<std::double, std::vector<std::double>>, std::float>> e = mysin(std::tuple<std::tuple<std::int, std::vector<std::int>>, std::float>>);
and so on...
In most examples about tuple templates, the function either has no return value, returns an accumulated value, or has the same return type as the input.
I've experimented a lot with these topics (among others):
Traversing nested C++11 tuple , c++11: building a std::tuple from a template function , How to make a function that zips two tuples in C++11 (STL)?
The last one has been especially useful. I've got this working for tuples, but not for recursive tuples (tuples in tuples).
Eventually (if this is possible at all), I'll have to make a mycos, mytan, myasin, etc. Using gcc 4.9.2.
**EDIT:**So this is what I came up with after Yakk's suggestions, and a little tweaking:
#include <utility>
#include <vector>
#include <memory>
#include <typeinfo> // used for typeid
#include <tuple>
#include <cstdlib> // for math functions?
#include <cmath> // for math functions
#include <type_traits> // for std::enable_if
template<class T , typename std::enable_if<std::is_integral<T>::value>::type* = nullptr >
double mysin(const T& t) { // note, function signature is unmodified
double a = t;
return std::sin(a);
// printing a debug string here will
// print tuple elements reversed!!
}
template<class T , typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr >
T mysin(const T& t) {// note, function signature is unmodified
// printing a debug string here will
// print tuple elements reversed!!
return std::sin(t);
}
struct sine_t {
template<class T>
auto operator()(T&&t)const->
decltype(mysin(std::declval<T>())) {
return mysin(std::forward<T>(t));
}
};
template<class F>
struct vectorize {
template<class T,
class R=std::vector< std::result_of_t< vectorize<F>(T const&) > >
>
R operator()( std::vector<T> const& v ) const {
R ret;
ret.reserve(v.size());
for( auto const& e : v ) {
ret.push_back( vectorize<F>{}(e) );
}
return ret;
}
template<
class X,
class R=std::result_of_t< F(X const&) >
>
R operator()( X const& x ) const {
return F{}(x);
}
template<
class R,
class... Ts,
size_t... Is
>
R tup_help( std::index_sequence<Is...>, std::tuple<Ts...> const& t ) const {
return std::make_tuple( vectorize<F>{}(std::get<Is>(t))... );
}
template<
class... Ts,
class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... >
>
R operator()( std::tuple<Ts...> const& t ) const {
return tup_help<R>( std::index_sequence_for<Ts...>{}, t );
}
};
//+++++++++++++++++++++++++++++++++++++++++
int main() {
std::vector<int> a = {1 ,2};
std::tuple<int, double, int, double> b (42, -3.14, 42, -3.14);
auto c = vectorize<sine_t>()(a);
auto d = vectorize<sine_t>()(b);
std::vector<std::tuple<int, int> > e {std::make_tuple(1 ,2)};
//This does not not work:
//auto f = vectorize<sine_t>()(e);
//This works:
std::tuple<std::vector<int> > g ( a );
auto f = vectorize<sine_t>()(g);
return 0;
}
This works. Needs c++14.
First an overload set object. This is useful because it lets us pass around the entire overload set as a single object:
struct sine_t {
template<class T>
auto operator()(T&&t)const->
decltype(mysin(std::declval<T>()))
{ return mysin(std::forward<T>(t)); }
};
next, we want to "vectorize" a given function object.
We'll start simple:
template<class F>
struct vectorize {
template<class T, class R=std::vector< std::result_of_t< F(T const&) > >>
R operator()( std::vector<T> const& v ) const {
R ret;
ret.reserve(v.size());
for( auto const& e : v ) {
ret.push_back( F{}(e) );
}
return ret;
}
template<class X, class R=std::result_of_t< F(X const&) >>
R operator()( X const& x ) const {
return F{}(x);
}
};
this supports 1 level recursion, and only on std::vector.
To allow infinite recursion of nested std::vectors, we modify the operator() overload for std::vector:
template<
class T,
class R=std::vector< std::result_of_t< vectorize<F>(T const&) > >
>
R operator()( std::vector<T> const& v ) const {
R ret;
ret.reserve(v.size());
for( auto const& e : v ) {
ret.push_back( vectorize<F>{}(e) );
}
return ret;
}
and now we support std::vector<std::vector<int>>.
For tuple support, we add 2 functions. The first one has this signature:
template<
class... Ts,
class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... >
>
R operator()( std::tuple<Ts...> const& t ) const
which gets us our return value (half the battle). To actually do the mapping, use the indexes trick:
template<
class R,
class... Ts,
size_t... Is
>
R tup_help( std::index_sequence<Is...>, std::tuple<Ts...> const& t ) const
{
return std::make_tuple( vectorize<F>{}(std::get<Is>(t))... );
}
template<
class... Ts,
class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... >
>
R operator()( std::tuple<Ts...> const& t ) const {
return tup_help<R>( std::index_sequence_for<Ts...>{}, t );
}
similar code for std::array and raw C arrays should work (converting the raw C array to a std::array naturally).
std::index_sequence etc is C++14, but easy to write a version that supports 100s of elements in C++11. (A version that supports large arrays takes more work). The result_of_t alias (and any similar) are also C++14, but the aliases are easy to write in C++11, or you can just typename std::result_of<?>::type verbose explosion.
In the end, you vectorize<sine_t>{} and pass in whatever.
If you want a function instead of a function object, simply have it delegate the work to vectorize<sine_t>.
Related
What is the shortest / best way to replace the n-th element of a tuple with a value (which may or may not have a different type)? Solutions including c++20 are fine.
[EDIT: I would prefer something not requiring other libraries, but I'm still interested what solutions are possible with e.g. boost].
I.e.:
#include <cassert>
#include <tuple>
template<std::size_t N, ... >
auto replace_tuple_element( ... ) // <- Looking for a suitable implementation
struct Foo {
int value;
};
int main()
{
auto t1 = std::tuple{ 0, 1, 2, 3 };
auto t2 = replace_tuple_element<2>( t1, Foo{10} );
assert( std::get<0>(t2) == std::get<0>(t1));
assert( std::get<1>(t2) == std::get<1>(t1));
assert( std::get<2>(t2).value == 10);
assert( std::get<3>(t2) == std::get<3>(t1));
}
Note: Just replacing the n-th type in a typelist has e.g. be discussed here: How do I replace a tuple element at compile time?.
But I also want to replace the value and hope that there are simpler/more elegant solutions now in c++20 than back when that question was asked.
One solution I found for c++20 is this:
#include <cassert>
#include <tuple>
#include <type_traits>
template<std::size_t N, class TupleT, class NewT>
constexpr auto replace_tuple_element( const TupleT& t, const NewT& n )
{
constexpr auto tail_size = std::tuple_size<TupleT>::value - N - 1;
return [&]<std::size_t... I_head, std::size_t... I_tail>
( std::index_sequence<I_head...>, std::index_sequence<I_tail...> )
{
return std::tuple{
std::get<I_head>( t )...,
n,
std::get<I_tail + N + 1>( t )...
};
}(
std::make_index_sequence<N>{},
std::make_index_sequence<tail_size>{}
);
}
struct Foo {
int value;
};
int main()
{
auto t1 = std::tuple{ 0, 1, 2, 3 };
auto t2 = replace_tuple_element<2>( t1, Foo{10} );
assert( std::get<0>(t2) == std::get<0>(t1));
assert( std::get<1>(t2) == std::get<1>(t1));
assert( std::get<2>(t2).value == 10);
assert( std::get<3>(t2) == std::get<3>(t1));
}
What I like about the solution is that it is a single, self containied function. I wonder if there is something even shorter and/or more readable though.
Possible solution:
template<std::size_t i>
using index = std::integral_constant<std::size_t, i>;
template<std::size_t N, class Tuple, typename S>
auto replace_tuple_element(Tuple&& tuple, S&& s) {
auto get_element = [&tuple, &s]<std::size_t i>(Index<i>) {
if constexpr (i == N)
return std::forward<S>(s);
else
return std::get<i>(std::forward<Tuple>(tuple));
};
using T = std::remove_reference_t<Tuple>;
return [&get_element]<std::size_t... is>(std::index_sequence<is...>) {
return std::make_tuple(get_element(index<is>{})...);
}(std::make_index_sequence<std::tuple_size_v<T>>{});
}
Note this decays all element types, removing references and const.
This amendment partially addresses this issue:
template<std::size_t N, class Tuple, typename S>
auto replace_tuple_element(Tuple&& tuple, S&& s) {
using T = std::remove_reference_t<Tuple>;
auto get_element = [&tuple, &s]<std::size_t i>(index<i>) {
if constexpr (i == N)
return std::forward<S>(s);
else
if constexpr (std::is_lvalue_reference_v<std::tuple_element_t<i, T>>)
return std::ref(std::get<i>(std::forward<Tuple>(tuple)));
else
return std::get<i>(std::forward<Tuple>(tuple));
};
return [&get_element]<std::size_t... is>(std::index_sequence<is...>) {
return std::make_tuple(get_element(index<is>{})...);
}(std::make_index_sequence<std::tuple_size_v<T>>{});
}
Now replace_tuple_element also follows the convention of std::make_tuple that converts std::reference_wrapper arguments into references. It does preserve reference types, but drops top-level constness.
struct Foo {
Foo(int i) : value(i) {}
int value;
};
int main() {
int i = 1;
int j = 2;
auto t1 = std::make_tuple(std::make_unique<Foo>(0), std::ref(i), std::cref(j), 4);
static_assert(std::is_same_v<decltype(t1),
std::tuple<std::unique_ptr<Foo>, int&, const int&, int>>);
auto t2 = replace_tuple_element<1>(std::move(t1), std::make_unique<Foo>(5));
static_assert(std::is_same_v<decltype(t2),
std::tuple<std::unique_ptr<Foo>, std::unique_ptr<Foo>, const int&, int>>);
auto t3 = replace_tuple_element<0>(std::move(t2), std::cref(i));
static_assert(std::is_same_v<decltype(t3),
std::tuple<const int&, std::unique_ptr<Foo>, const int&, int>>);
auto t4 = replace_tuple_element<2>(std::move(t3), i);
static_assert(std::is_same_v<decltype(t4),
std::tuple<const int&, std::unique_ptr<Foo>, int, int>>);
}
Full demo with run-time asserts
This should do it:
template<std::size_t N, class U, class T>
auto replace_tuple_element(T&& t, U&& u) {
return [&]<std::size_t... I>(std::index_sequence<I...>) {
return std::tuple<std::conditional_t<I == N, U, std::tuple_element_t<I, std::decay_t<T>>>...>{
[&]() -> decltype(auto) {
if constexpr (I == N) return std::forward<U>(u);
else return static_cast<std::tuple_element_t<I, std::decay_t<T>>>(std::get<I>(t));
}()...};
}(std::make_index_sequence<std::tuple_size_v<std::decay_t<T>>>{});
}
You can remove some of the casts, forwards etc. if you're only concerned with value semantics.
The only thing new here is lambda template parameters to infer the indexing argument.
If we want to both preserve all the types exactly as they are, and also do the same kind of reference unwrapping thing that the standard library typically does, then we need to make a small change to what the other implementations are here.
unwrap_ref_decay will do a decay_t on the type, and then turn reference_wrapper<T> into T&. And using Boost.Mp11 for a few things that just make everything nicer:
template <size_t N, typename OldTuple, typename NewType>
constexpr auto replace_tuple_element(OldTuple&& tuple, NewType&& elem)
{
using Old = std::remove_cvref_t<OldTuple>;
using R = mp_replace_at_c<Old, N, std::unwrap_ref_decay_t<NewType>>;
static constexpr auto Size = mp_size<Old>::value;
auto get_nth = [&](auto I) -> decltype(auto) {
if constexpr (I == N) return std::forward<NewType>(elem);
else return std::get<I>(std::forward<OldTuple>(tuple));
};
return [&]<size_t... Is>(std::index_sequence<Is...>) {
return R(get_nth(mp_size_t<Is>())...);
}(std::make_index_sequence<Size>());
}
This implementation means that given:
std::tuple<int const, int const> x(1, 2);
int i = 42;
auto y = replace_tuple_element<1>(x, std::ref(i));
y is a tuple<int const, int&>.
This is a good use case for a counterpart of tuple_cat that, instead of concatenating tuples, gives you a slices of a tuple. Unfortunately, this doesn't exist in the standard library, so we'll have to write it ourselves:
template <std::size_t Begin, std::size_t End, typename Tuple>
constexpr auto tuple_slice(Tuple&& t)
{
return [&]<std::size_t... Ids> (std::index_sequence<Ids...>)
{
return std::tuple<std::tuple_element_t<Ids, std::remove_reference_t<Tuple>>...>
{std::get<Begin + Ids>(std::forward<Tuple>(t))...};
} (std::make_index_sequence<End - Begin>{});
}
Just like tuple_cat, this preserve the exact same types of the original tuple.
With tuple_cat and tuple_slice, the implementation of replace_tuple_element feels quite elegant:
template <std::size_t N, typename Tuple, typename T>
constexpr auto replace_tuple_element(Tuple&& tuple, T&& t)
{
constexpr auto Size = std::tuple_size_v<std::remove_reference_t<Tuple>>;
return std::tuple_cat(
tuple_slice<0, N>(std::forward<Tuple>(tuple)),
std::make_tuple(std::forward<T>(t)),
tuple_slice<N + 1, Size>(std::forward<Tuple>(tuple))
);
}
Using make_tuple preserves the behavior of turning reference_wrapper<T> into T&. Demo
I would like to write function as this find:
multi_set<int, string, double, myType> m; //vector of tuples
m.insert(/*some data*/);
m.find<1,2>("something",2.123);
Or
m.find<0,3>(1,instanceOfMyType);
m.find<1>("somethingelse");
Where find can be parametrized corresponding to any subset of tuple parameters.
My code so far:
template <typename ... T>
class multi_set{
typedef tuple < T... > Tuple;
vector<tuple<T...>> data = vector<tuple<T...>>();
public:
void insert(T... t){
data.push_back(tuple<T...>(t...));
}
template<size_t ... Pos>
void find(???){
// then I would like to use those params to search through data and
// return first matching item
}
}
// test whether a particular tuple is a match
template<size_t... Pos>
static bool is_match(const Tuple& tuple, const typename std::tuple_element<Pos, Tuple>::type &... args) {
std::initializer_list<bool> results = { (std::get<Pos>(tuple) == args)... };
return std::all_of(results.begin(), results.end(), [](bool p) { return p; });
}
// Find the first one that is a match.
template<size_t... Pos>
typename vector<Tuple>::const_iterator find(const typename std::tuple_element<Pos, Tuple>::type &... args) const {
return std::find_if(data.begin(), data.end(), [&](const Tuple & tup) { return is_match<Pos...>(tup, args...); });
}
It's also possible to have find take a type parameter pack and perfectly forward, rather than taking fixed types with tuple_element. The benefit is that you can avoid an unnecessary conversion if == is transparent. The cost is that you can't take anything that can't be perfectly forwarded any more (e.g., braced initializer lists, 0 as a null pointer constant). A side benefit appears to be that MSVC 2013 doesn't choke on this version:
// test whether a particular tuple is a match
template<size_t... Pos, class... Args>
static bool is_match(const Tuple& tuple, Args&&... args) {
std::initializer_list<bool> results = { (std::get<Pos>(tuple) == std::forward<Args>(args))... };
return std::all_of(results.begin(), results.end(), [](bool p) { return p; });
}
// Find the first one that is a match.
template<size_t... Pos, class... Args>
typename vector<Tuple>::const_iterator find(Args&&... args) const {
return std::find_if(data.begin(), data.end(), [&](const Tuple & tup) { return is_match<Pos...>(tup, std::forward<Args>(args)...); });
}
You should look into boost::multi_index. It is very close to what you are looking for.
http://www.boost.org/doc/libs/1_54_0/libs/multi_index/doc/tutorial/index.html
This is a function that takes a seed value, and a set of lambdas. It feeds that seed value through each of the lambdas in turn:
template<class... Fs, class R>
R chain( R r, Fs&&... fs ) {
using in_order = int[];
(void)(in_order{0,
(
(r = std::forward<Fs>(fs)( r ))
, void(), 0
)...
});
return r;
}
Inside your class, we use the above:
template<size_t... Pos, class...Us>
typename std::vector<Tuple>::const_iterator
find(Us const&... us) const {
return std::find_if(
data.begin(), data.end(),
[&](const Tuple & tup) {
return chain(
true,
[&](bool old){
return old && (std::get<Pos>(tup) == us);
}...
);
}
);
}
this compiles in clang, but not g++ 4.9.2 -- g++ doesn't like parameter packs inside lambdas.
Note the fact we take Us const&... -- this allows for transparent ==, which is important in some cases. std::string == char const* is a classic example, where if you force find to take the same value as in the tuple, you'll force a needless allocation in calling find.
In C++1z, the chain call can be replaced with:
( ... && (std::get<Pos>(tup) == us) )
which is conceptually identical, but much easier to read. This is known as a "fold expression".
Now, a problem with the above is that it uses forwarding references, which causes imperfect forwarding problems of perfect forwarding.
The most annoying of which is the inability to use {} to construct arguments.
If we use matching types, we instead force non-transparent comparison, which can be expensive (examine std::string compared to "hello this is a c string" -- it causes possibly allocation if we force the c string into a std::string.)
A way around this is to type erase down to the concept of equality with a given type.
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<class T>struct tag{using type=T;};
template<class...>struct types{using type=types;};
template<class T>
using block_deduction = typename tag<T>::type;
template<class F, class Sig, class T=void>
struct erase_view_op;
template<class F, class R, class...Ts, class T>
struct erase_view_op<F, R(Ts...), T>
{
using fptr = R(*)(void const*, Ts&&...);
fptr f;
void const* ptr;
private:
template<class U>
erase_view_op(U&& u, int):
f([](void const* p, Ts&&...ts)->R{
U& u = reinterpret_cast<U&>( *static_cast<std::decay_t<U>*>(const_cast<void*>(p)) );
return F{}( u, std::forward<Ts>(ts)... );
}),
ptr( static_cast<void const*>(std::addressof(u)) )
{}
public:
template<class U, class=std::enable_if_t< !std::is_same<std::decay_t<U>,erase_view_op>{} && std::is_convertible< std::result_of_t<F(U,Ts...)>, R >{} >>
erase_view_op(U&& u):erase_view_op( std::forward<U>(u), 0 ){}
template<class U=T, class=std::enable_if_t< !std::is_same<U, void>{} >>
erase_view_op( block_deduction<U>&& u ):erase_view_op( std::move(u), 0 ){}
erase_view_op( erase_view_op const& ) = default;
erase_view_op( erase_view_op&& ) = default;
R operator()( Ts... ts ) const {
return f( ptr, std::forward<Ts>(ts)... );
}
};
struct equality {
template<class lhs, class rhs>
bool operator()(lhs const& l, rhs const& r)const {
return l==r;
}
};
template<class T>
using erase_equal_to = erase_view_op< equality, bool(T const&), T >;
using string_equal_to = erase_equal_to< std::string >;
int main() {
static_assert( std::is_same< bool, std::result_of_t< std::equal_to<>(decltype("hello"), std::string const&) > >{}, "hmm" );
string_equal_to s = "hello";
string_equal_to s2 = {{"hello"}};
(void)s2;
std::string x = "hello";
std::string y = "jello";
std::cout << s(x) << s(y) << '\n';
}
then we rewrite find:
template<size_t... Pos>
typename std::vector<Tuple>::const_iterator
find(erase_equal_to< std::remove_reference_t<std::tuple_element_t<Pos, Tuple>> >... us) const {
return std::find_if(
data.begin(), data.end(),
[&](const Tuple & tup) {
return chain(
true,
[&](bool old){
return old && us(std::get<Pos>(tup));
}...
);
}
);
}
which does both transparent equality and allows {} based construction (well, it does require {{}} based construction -- the outer to say we are constructing the eraser, the inner to construct the T).
Sometimes you want a function to return multiple values. One very common way to
achieve such a behavior in C++ is to pass your values by non-const reference and
assign to them in your function:
void foo(int & a, int & b)
{
a = 1; b = 2;
}
Which you would use:
int a, b;
foo(a, b);
// do something with a and b
Now I have a functor that accepts such a function and would want to forward the
set arguments into another function returning the result:
template <typename F, typename G>
struct calc;
template <
typename R, typename ... FArgs,
typename G
>
struct calc<R (FArgs...), G>
{
using f_type = R (*)(FArgs...);
using g_type = G *;
R operator()(f_type f, g_type g) const
{
// I would need to declare each type in FArgs
// dummy:
Args ... args;
// now use the multiple value returning function
g(args...);
// and pass the arguments on
return f(args...);
}
};
Does this approach even make sense, or should I rather use a tuple based
approach? Is there something smarter than a tuple based approach here?
You could use compile-time indices:
template< std::size_t... Ns >
struct indices
{
typedef indices< Ns..., sizeof...( Ns ) > next;
};
template< std::size_t N >
struct make_indices
{
typedef typename make_indices< N - 1 >::type::next type;
};
template<>
struct make_indices< 0 >
{
typedef indices<> type;
};
template< typename F, typename G >
struct calc;
template<
typename R, typename ... FArgs,
typename G
>
struct calc< R (FArgs...), G >
{
using f_type = R (*)(FArgs...);
using g_type = G *;
private:
template< std::size_t... Ns >
R impl(f_type f, g_type g, indices< Ns... > ) const
{
std::tuple< FArgs ... > args;
g( std::get< Ns >( args )... );
// alternatively, if g() returns the tuple use:
// auto args = g();
return f( std::get< Ns >( args )... );
}
public:
R operator()(f_type f, g_type g) const
{
return impl( f, g, typename make_indices< sizeof...( FArgs ) >::type() );
}
};
When accepting the fact that we're changing the signature of both f and g to work with std::tuple, the answer to this problem becomes trivial:
template <typename F, typename G> struct calc;
template <typename R, typename ... Args>
struct calc<R (std::tuple<Args...> const &), std::tuple<Args...> ()>
{
using f_type = R (*)(std::tuple<Args...> const &);
using g_type = std::tuple<Args...> (*)();
R operator()(f_type f, g_type g) const
{
return f(g());
}
};
Here is a simple example:
int sum(std::tuple<int, int> const & t) { return std::get<0>(t) + std::get<1>(t); }
std::tuple<int, int> gen() { return std::make_tuple<int, int>(1, 2); }
auto x = calc<decltype(sum), decltype(gen)>()(&sum, &gen);
The limitation of this solution is clear however: you have to write your own functions. Using something like std::pow as f is not possible using this approach.
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;
}