C++ functional & generic programming [with MySQL connector example] - c++

I am going to use MySQL connector. They provide functions to access the result row. Some examples are getString(1), getInt(1), getDate(2). The number inside the parenthesis is about the index of the result.
So that I have to use the following code to access this example row: 'John', 'M', 34
string name = row.getString(1);
string sex = row.getString(2);
int age = row.getInt(3);
I would like to try generic programming for various reasons (mainly for fun). But it was quite disappointing that I can't make it happens even with much time used.
The final result that I want:
std::tie<name, sex, age> = row.getResult<string, string, int>();
This functions should call the corresponding MySQL API.
It is also good to see any answer similar to below, although the syntax is wrong.
std::tie<name, sex, age> = row.getResult([string, string, int]);
Please don't suggest using for-loop. Let's try something more generic & functional ;-)

This works for me:
struct Row
{
template <int N, typename ... Args> struct Helper;
template <typename Arg1> struct Helper<1, Arg1>
{
static std::tuple<Arg1> getResult(Row& r)
{
return std::make_tuple(r.getResult<Arg1>(0));
}
};
template <int N, typename Arg1, typename ... Args>
struct Helper<N, Arg1, Args...>
{
static std::tuple <Arg1, Args ...> getResult(Row& r)
{
return std::tuple_cat(std::make_tuple(r.getResult<Arg1>(N-1)),
Helper<N-1, Args...>::getResult(r));
}
};
template <typename Arg> Arg getResult(int index)
{
// This is where the value needs to be extracted from the row.
// It is a dummy implementation for testing purposes.
return Arg{};
}
template <typename ... Args>
std::tuple <Args ...> getResult()
{
return Helper<sizeof...(Args), Args...>::getResult(*this);
}
};
Example usage:
Row r;
auto res1 = r.getResult<std::string>();
auto res2 = r.getResult<int>();
auto res3 = r.getResult<int, double, int>();
auto res4 = r.getResult<int, int, double, double>();
auto res5 = r.getResult<std::string, std::string, int, int, double, double>();
Working code: http://ideone.com/6IpJ8q

First you need to write these:
template<class T> T get( MySQLRow const& row, unsigned index);
template<>
int get<int>( MySQLRow const& row, unsigned index) { return connector.GetInt(index); }
// etc
Then, add some template meta programming tools:
template<class...>struct types{using type=types;};
the above could probably be replaced with std::tuple<?>* instead of types<?>. Maybe. But the above is clearer anyhow.
Next, this can be replaced with C++14's integral_sequence:
template<unsigned...>struct indexes{using type=indexes;};
template<unsigned max, unsigned...is>struct make_indexes<max-1, max-1, is...>{};
template<unsigned...is>struct make_indexes<0,is...>:indexes<is...>{};
template<unsigned max>using make_indexes_t=typename make_indexes<max>::type;
Finally, stuff:
namespace details {
template<unsigned... Is, class... Types>
std::tuple< Types... >
getResult( indexes<Is...>, types<Types...>, MySQLRow const& row ) {
return { get<Types>( row, Is+1 )... };
}
}
template<class... Types>
std::tuple<Types...>
getResult( MySQLRow const& row ) {
return details::getResult( make_indexes_t<sizeof...(Ts)>{}, types<Types...>{}, row );
}
syntax is:
getResult<int, double, std::string>( row );
assuming you write the various get functions and fix the type of MySQLRow to whatever it really is, and assuming the 1st row is 1.

Create the set of function Get overloads which implement unified interface to GetX methods of row:
#define DEFINE_GET_FOR_TYPE(Type, TypeName) \
void Get(Type& arg, const MySQLRow& row, std::size_t index) \
{ \
arg = row.Get##TypeName(index); \
}
DEFINE_GET_FOR_TYPE(int, Int)
DEFINE_GET_FOR_TYPE(std::string, String)
// ...
Here the macro DEFINE_GET_FOR_TYPE is used to create the necessary set.
Implement GetResult function template using C++14 std::index_sequence and std::make_index_sequence (they can be implemented in C++11 program, too):
struct Iterate
{
Iterate(...) {}
};
template <std::size_t... indices, typename... Types>
void GetResultImpl(const MySQLRow& row, std::index_sequence<indices...>, Types&... args)
{
Iterate{(Get(args, row, indices + 1), 0)...};
}
template <typename... Types>
void GetResult(const MySQLRow& row, Types&... args)
{
GetResultImpl(row, std::make_index_sequence<sizeof...(Types)>(), args...);
}
Use GetResult function template to get values from the row:
std::string name;
std::string sex;
int age;
GetResult(row, name, sex, age);
Live demo

Related

Variadic function templates with two (or more) specific packs (specialization/overloading)

The function 'Process' is taking a variable number of arguments of variable type. To handle different cases, I have successfully overloaded it like this:
// general case
template <typename ...Types>
void Process( const Types&... items )
// single T
template <typename T>
void Process( const T& t )
// one or more of type NVP<>
template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps )
What I want to do - but can't - is the following: I need an overload for cases with any number of leading arguments of a types ATT<> followed by any number of NVP<> like this:
// any number of leading Types ATT<> followed by any number of NVP<>
template <typename ...ATypes, typename ...BTypes>
void Process( const ATT<ATypes>&... atts, const NVP<BTypes>&... nvps )
At first you would think it should be 'easy' for a compiler to match this, if it can already do the other cases. There should be absolutely no ambiguity here!? However, the matching fails, no error messages, but the desired overload it is just ignored by the compiler.
Currently using VS2017 with /std:c++17
Notes:
1. It can, obviously, be done for one leading type ATT<T1> like this
// one leading Type ATT<T1>
template <typename T1, typename ...Types>
void Process( const ATT<T1>& a1, const Types&... remaining )
But for more than one, I need to do some ugly manual recursion. I really want to have the whole pack of leading ATT<...>.
2. I am aware that a leading parameter pack - of general types - always is ambiguous for matching, but for a specialization like ATT<ATypes>... no ambiguity should exist.
You could dispatch from the const Types&... overload based on if Types... matches ATT<T>..., NVP<U>....
The basic strategy here is finding the index of the last ATT<T>, forwarding everything as a tuple, then indexing with the appropriate index sequence to forward to another function where the ATT values and NVP values are in two tuples:
namespace detail {
template<class...>
struct get_split_index;
template<class T, class... Others>
struct get_split_index<T, Others...> {
static constexpr std::size_t i = -1;
};
template<class T, class... Others>
struct get_split_index<ATT<T>, Others...> {
static constexpr std::size_t next = get_split_index<Others...>::i;
static constexpr std::size_t i = next == -1 ? -1 : next + 1u;
};
template<class T, class... Others>
struct get_split_index<NVP<T>, Others...> {
// will be 0 if the rest are all NVP<T>, otherwise -1
static constexpr std::size_t i = get_split_index<Others...>::i;
};
template<>
struct get_split_index<> {
static constexpr std::size_t i = 0;
};
template<typename... ATypes, typename... BTypes, std::size_t... ATT_I, std::size_t... NVP_I>
void Process(const std::tuple<const ATT<ATypes>&...>& att, const std::tuple<const NVP<BTypes>&...>& nvp, std::index_sequence<ATT_I...>, std::index_sequence<NVP_I...>) {
// Use (std::get<ATT_I>(att)) and (std::get<NVP_I>(nvp))
// instead of (atts) and (nvps) that you would use in your
// supposed `void Process(const ATT<ATypes>&..., const NVP<BTypes>&...)`
}
template<typename... Types, std::size_t... ATT_I, std::size_t... NVP_I>
void ProcessDispatch(const std::tuple<Types...>& t, std::index_sequence<ATT_I...> att_i, std::index_sequence<NVP_I...> nvp_i) {
detail::Process(std::forward_as_tuple(std::get<ATT_I>(t)...), std::forward_as_tuple(std::get<NVP_I + sizeof...(ATT_I)>(t)...), att_i, nvp_i);
}
}
template <typename ...Types>
void Process( const Types&... items ) {
constexpr std::size_t split_index = detail::get_split_index<Types...>::i;
if constexpr (split_index != -1) {
// Might want to check `&& sizeof...(Types) != 0`
detail::ProcessDispatch(std::forward_as_tuple(items...), std::make_index_sequence<split_index>{}, std::make_index_sequence<sizeof...(Types) - split_index>{});
} else {
// general case
}
}
template <typename T>
void Process( const T& t ) {
// single T
}
template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps ) {
// one or more of type NVP<>
// This can also be folded into `detail::Process`, checking
// `if constexpr (sizeof...(BTypes) == 0)`.
}
Believe you can use a struct to help you here. The compiler can't determine where one parameter pack stops and the other begins, consider:
foo(1, 2.0, '3', "45", 6.0f). The first parameter pack could be nothing, the first, all of them or none of the above. There is no particular reason to prefer one over another. So you can't make a function that accepts two variadics. What you can do, is to split it into two structs, and specify explicitly the arguments for the outer class.
template<typename... Args>
struct S
{
template<typename... Inner>
static void Process(const ATT<Args>&... atts, const NVP<Inner>&... nvps) {}
};
Example for usage:
ATT<double> a1;
ATT<long> a2;
NVP<int> n1;
NVP<const char*> n2;
S<double, long>::Process(a1, a2, n1, n2);
Another version could be by using the constructor. Here, you also get auto-deduction which is easier. Unfortunately, it only works from C++17 and above.
template<typename... Args>
struct S
{
std::tuple<ATT<Args>...> tup;
S(const ATT<Args>&... atts)
: tup(atts...)
{}
template<typename... Inner>
void Process(const NVP<Inner>&... nvps){}
};
template<typename... Args>
S(const ATT<Args>&... atts)->S<Args...>;
And the usage is:
S(ATT(1), ATT(3.4)).Process(NVP("asdf"), NVP(3.4), NVP('f'));
return 0;
Assuming you're OK with getting them as tuples I made this after drawing from https://stackoverflow.com/a/12782697/1480324 :
#include <iostream>
#include <tuple>
template<typename T>
struct ATT {};
template<typename T>
struct NVP {};
template<typename... ATTs, typename... NVPs>
void Process(const std::tuple<ATT<ATTs>...>& atts, const std::tuple<NVP<NVPs>...>& nvps) {
std::cout << sizeof...(ATTs) << std::endl;
std::cout << sizeof...(NVPs) << std::endl;
}
int main() {
Process(std::make_tuple(ATT<int>(), ATT<double>()), std::make_tuple(NVP<std::string>(), NVP<bool>()));
return 0;
}
It compiles on https://www.onlinegdb.com/online_c++_compiler , but I can't test in visual studio.

variadic template arguments with default values

I have a templated struct that has a method that (along with other parameters) accepts those templated parameters.
template<class... Types>
struct Observer
{
void subscribe(const std::string& id, Types... args)
{
// store id somehow
data = std::make_tuple(args...);
}
std::tuple<Types...> data;
};
I want to make all the templated arguments optional. So that it looks like this:
Observer<float, int> observer;
observer.subscribe("id1", 3.14, 4);
observer.subscribe("id2", 0.707); // this doesn't work!
observer.subscribe("id3"); // this doesn't work!
As far as I know there is not straight forward way? But maybe someone know a workaround or a trick.
Ideally, I'd like to provide my own defaults. Maybe like this:
enum class SomeEnum { Val1, Val2 };
Observer<float, SomeEnum, 0.f, SomeEnum::Val1> observer;
observer.subscribe("id1", 3.14);
Here is LIVE EXAMPLE.
In C++17 you can simply do something like:
template<class... Types>
struct Observer
{
static constexpr std::tuple<Types...> defaults{42, 24, 99};
template<class... Args>
void subscribe(Args... args)
{
if constexpr (sizeof...(Types) > sizeof...(Args)) {
subscribe(args..., std::get<sizeof...(Args)>(defaults));
} else {
// whatever you need with `args...`
}
}
};
Here I am simply picking them from Observer::defaults, but feel free to compute them however you want.
For C++14 and below, you will need to emulate the if constexpr. See e.g. Constexpr if alternative for alternatives.
Boost.Mp11 for the win:
template <typename... Ts>
void subscribe(const std::string& id, Ts const&... args)
{
static_assert(sizeof...(Ts) <= sizeof...(Types));
using Rest = mp_drop_c<std::tuple<Types...>, sizeof...(Ts)>;
data = std::tuple_cat(std::make_tuple(args...), Rest());
}
The assumes that value initialization of the trailing arguments is fine. If it's not, you'll have to figure out what to do with Us.
Works nicer if you actually make the optional more explicit:
template<class... Types>
struct Observer
{
using Data = std::tuple<std::optional<Types>...>;
template <typename... Ts>
void subscribe(const std::string& id, Ts const&... args)
{
static_assert(sizeof...(Ts) <= sizeof...(Types));
using Rest = mp_drop_c<Data, sizeof...(Ts)>;
data = std::tuple_cat(std::make_tuple(args...), Rest());
}
Data data;
};

Get function with generic return type

I try to implement a data structure that comprises multiple name-value pairs where values may differ in their type:
template< typename T >
struct name_value_pair
{
std::string name;
T value;
};
template< typename... Ts >
class tuple_of_name_value_pairs
{
public:
/* type of value */ get_value( std::string n )
{
// return the value that the element in
// _name_value_pairs with name "n" comprises
}
private:
std::tuple<Ts...> _name_value_pairs:
};
Unfortunately, I have no idea how to implement the get function.
A workaround would be to state names as integers instead of strings and use an implementation according to std::get but this no option here: the input type of get has to be a string.
Has anyone an idea?
Firstly have in mind you cannot do what you want directly. C++ is a strongly typed language so type of function result must be known at compile time. So of course if the string you pass to the getter is known at runtime you're not able to dispatch function at compile time to let compiler deduce appropriate result type. But when you accept that you need type-erasure to erase the getter result type you could make use of e.g. boost::variant to deal with your problem. C++14 example (using boost, since c++17 variant should be available in std):
#include <boost/variant.hpp>
#include <utility>
#include <iostream>
#include <tuple>
template< typename T >
struct name_value_pair
{
using type = T;
std::string name;
T value;
};
template <std::size_t N, class = std::make_index_sequence<N>>
struct getter;
template <std::size_t N, std::size_t... Is>
struct getter<N, std::index_sequence<Is...>> {
template <class Val, class Res>
void setRes(Val &val, Res &res, std::string &s) {
if (val.name == s)
res = val.value;
}
template <class Tup>
auto operator()(Tup &tuple_vals, std::string &s) {
boost::variant<typename std::tuple_element<Is, Tup>::type::type...> result;
int helper[] = { (setRes(std::get<Is>(tuple_vals), result, s), 1)... };
(void)helper;
return result;
}
};
template <std::size_t N, class = std::make_index_sequence<N>>
struct setter;
template <std::size_t N, std::size_t... Is>
struct setter<N, std::index_sequence<Is...>> {
template <class Val, class SVal>
std::enable_if_t<!std::is_same<SVal, typename Val::type>::value> setVal(Val &, std::string &, const SVal &) { }
template <class Val>
void setVal(Val &val, std::string &s, const typename Val::type &sval) {
if (val.name == s)
val.value = sval;
}
template <class Tup, class Val>
auto operator()(Tup &tuple_vals, std::string &s, const Val &val) {
int helper[] = { (setVal(std::get<Is>(tuple_vals), s, val), 1)... };
(void)helper;
}
};
template <class T, class Res>
using typer = Res;
template< typename... Ts >
class tuple_of_name_value_pairs
{
public:
auto get_value( std::string n )
{
return getter<sizeof...(Ts)>{}(_name_value_pairs, n);
}
template <class T>
void set_value( std::string n, const T& value) {
setter<sizeof...(Ts)>{}(_name_value_pairs, n , value);
}
void set_names(typer<Ts, std::string>... names) {
_name_value_pairs = std::make_tuple(name_value_pair<Ts>{names, Ts{}}...);
}
private:
std::tuple<name_value_pair<Ts>...> _name_value_pairs;
};
int main() {
tuple_of_name_value_pairs<int, float, double> t;
t.set_names("abc", "def", "ghi");
t.set_value("abc", 1);
t.set_value("def", 4.5f);
t.set_value("ghi", 5.0);
std::cout << t.get_value("def") << std::endl;
}
[live demo]
I'm sure you'll be able to optimise the code (e.g. make use of move semantics/perfect forwarding, etc.). This is only to present you how to get your implementation started.

Template functions with paired parameters

I'm trying to make a C++ function that accepts an unknown number of parameters total, but that they are always paired with specific types.
// logically, this is what the template Pair would be
// template<int, std::string> struct Pair {};
// desired:
// accept a const char * as a first parameter, and then in pairs ...
// integer, const char *
template <typename... Arguments> unsigned int onlyInPairs
(const std::string name, const Arguments& ... args) {
const unsigned numargs = sizeof...(Arguments);
// more magic would happen here with the parameters :)
return numargs;
}
int _tmain(int argc, _TCHAR* argv[])
{
// only string, [num, string] [num, string] should work
// desire that the syntax be as simple as shown, and not require
// extra classes to be created (like a Tuple) for each pair.
// this should work...
auto count = onlyInPairs("ABC", 1, "DEF", 2, "HIJ"); // works
// this should not work, as it's not number, string
count = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2);
return 0;
}
I've looked at parameter packs (reference), but can't seem to apply the documentation I've found to my specific problem. I'd like to try to catch the problem at compile time if the parameters are not specified correctly.
The goal was to use a syntax that was free of template noise as much as possible as the "pairs" will always be this way (and the programmer will know that). So, we wanted to just have int, string (repeat).
Ideally, the solution would work with Visual Studio 2013's C++ compiler, but I'd accept any answer that works and demonstrates the current possible shortcomings of VS C++ related to this issue.
Appendix - More details
The code being written would ultimately be often read by tech-savvy, but not formally trained C/C++ programmers (like a technical support). So, we're trying to get it to be distraction free as much as possible. There can be 2-16 pairs of values ... so keeping it distraction free and just the data is desirable.
Here's one possibility. Class template Enforce recursively inherits from itself and applies static_assert on pairs of template arguments until the specialization is picked that doesn't do anything:
#include <type_traits>
#include <string>
template<typename...Args>
struct Enforce;
template<typename T, typename T1, typename T2, typename... Args>
struct Enforce<T, T1, T2, Args...> : Enforce<T, Args...> {
static_assert( std::is_constructible<T, T2>::value, "Wrong T2!");
};
template<typename T>
struct Enforce<T> {
};
template <typename... Arguments>
void onlyInPairs (const std::string name, const Arguments& ... args)
{
Enforce<std::string, Arguments...>();
}
int main()
{
onlyInPairs("this", 1, "works", 2, "fine");
//onlyInPairs("this", 1, "doesn't", 2, 3);
}
Instead of recursive inheritance, you can use recursive typedef instead. At least in gcc, that ought to compile faster and with less noise (warning about non-virtual destructor in base class, etc.).
EDIT:
Here's another version that ANDs the checks together and saves the result:
template<typename...Args>
struct Enforce;
template<typename T, typename T1, typename T2, typename... Args>
struct Enforce<T, T1, T2, Args...> {
static const bool value =
std::is_constructible<T,T2>::value &&
Enforce<T, Args...>::value;
};
template<typename T>
struct Enforce<T> : std::true_type {
};
Now you can move the assert closer, inside onlyInPairs:
template <typename... Arguments>
void onlyInPairs (const std::string name, const Arguments& ... args)
{
static_assert( Enforce<std::string, Arguments...>::value , "Wrong second arg..." );
}
What template noise do you speak of?
void onlyInPairs(std::initializer_list<std::pair<int, std::string>>&& pairs) {}
int main() {
onlyInPairs({
{1, "abc"},
{2, "def"},
{3, "foo"},
});
}
Use compile time recursion:
void processArgPairs() {
// to stop recursion
}
template <typename Arg1, typename Arg2, typename... Arguments>
void processArgPairs(Arg1 a, Arg2 b, Arguments&& ...args){
static_assert(std::is_constructible<int, Arg1>::value, "Wrong type of first argument - int expected");
static_assert(std::is_constructible<std::string, Arg2>::value, "Wrong type of second argument - string expected
processArgPairs(std::forward<Arguments>(args)...);
}
template <typename... Arguments> unsigned int onlyInPairs
(const std::string name, Arguments&& ... args) {
const unsigned numargs = sizeof...(Arguments);
processArgPairs(std::forward<Arguments>(args)...);
return numargs;
}
Something like this?
template <typename... Arguments>
unsigned int onlyInPairs(const std::string name, const Arguments& ... args)
{
const unsigned numargs = sizeof...(Arguments);
check(args...);
return numargs;
}
template <typename... Arguments>
void check(const int i, const std::string name, const Arguments& ... args)
{
check(args...);
}
void check(const int i, const std::string name)
{
}
int main()
{
auto count = onlyInPairs("ABC", 1, "DEF", 2, "HIJ"); // works
count = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2); //compile error
return 0;
}
This is a fairly old-school solution: using is_convertible should be cleaner
#include <string>
template <typename... Args> struct EnforcePairsHelper;
// terminal case
template <> struct EnforcePairsHelper<> {
enum { size = 0 };
};
// multiple specializations for reliable matching:
// only the last is really required here
template <typename... ArgTail>
struct EnforcePairsHelper<int, const char *, ArgTail...> {
enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <typename... ArgTail>
struct EnforcePairsHelper<int, char *, ArgTail...> {
enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <int N, typename... ArgTail>
struct EnforcePairsHelper<int, char [N], ArgTail...> {
enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <typename... Args> unsigned onlyInPairs (const std::string name,
const Args& ... args) {
const unsigned numargs = EnforcePairsHelper<Args...>::size;
// more magic would happen here with the parameters :)
return numargs;
}
int main() {
unsigned ok = onlyInPairs("ABC", 1, "DEF", 2, "HIJ");
// unsigned no = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2);
}

Is it possible to iterate over all elements in a struct or class?

Is it possible to iterate over all elements in a struct or class?
For example if I have a struct of three elements of different type:
struct A {
classA a;
classB b;
classC c;
};
then I need some iterator such that a method next() would give me the value
of the next element. The problem is that as you see, the values have different types.
Nope, not with the language as it is.
You could do it by deriving your classes from a common base, and then implementing your own iterator to return pointers to each item as the iterator is traversed.
Alternatively put the items in a std::vector and use that to provide the iteration.
No, there is no reflection in C++, (yet, there are murmurs about static reflection coming one day).
Anyway, there is a way to work around this, to an extent - first of all, you'll need a (temporary) tuple with references to your data members.
Then you will need a construct "iterating" over the tuple, such as:
void applyToAll() { }
template <typename Lambda, typename... Lambdas>
void applyToAll(Lambda&& closure, Lambdas&&... closures) {
std::forward<Lambda>(closure)();
applyToAll(std::forward<Lambdas>(closures)...);
}
// use your favourite sequence-making trick
template <unsigned... Is>
struct _Sequence {
typedef _Sequence<Is...> type;
};
template <unsigned Max, unsigned... Is>
struct _MakeSequence : _MakeSequence<Max - 1, Max - 1, Is...> { };
template <unsigned... Is>
struct _MakeSequence<0, Is...> : _Sequence<Is...> { };
template <typename Tuple, typename Functor, unsigned... Is>
void _foreachElemInTuple(_Sequence<Is...>, Tuple&& t, Functor&& f) {
applyToAll(
[&]{ std::forward<Functor>(f)(std::get<Is>(std::forward<Tuple>(t))); }...
);
}
template <typename Tuple, typename Functor>
void foreachElemInTuple(Tuple&& t, Functor&& f) {
_foreachElemInTuple(
_MakeSequence<std::tuple_size<
typename std::decay<Tuple>::type>::value>(),
std::forward<Tuple>(t), std::forward<Functor>(f)
);
}
Then you can call foreachElemInTuple(yourTuple, some_adapter()).
Your adapter will look like:
struct some_adapter {
template <typename... Args>
// A little bit of C++14, you can also just -> decltype the thing
decltype(auto) operator()(Args&& ... args) const {
return doStuff(std::forward<Args>(args)...);
}
};
As everyone else says, you cannot directly iterate over data members of a
class. However, it is not difficult to do it indirectly, provided of course that
you can access each of the data members you want to iterate over. The idea
in essense, as per ScarletAmaranth's solution, is to iterate over an std::tuple
of references to those data members.
The following program shows how to obtain such a tuple, using std::forward_as_tuple,
and another way to do the iterating by compiletime recursion, without
auxiliary apparatus.
#include <tuple>
/* You want to be able do something with the values of the members of an `A`
in turn.
*/
struct A
{
char ch;
int i;
double d;
// May also have members of class type. It doesn't matter
};
/* 1) Provide yourself with the means of creating a sequence that contains
references to the data members of a given `A`
*/
std::tuple<char const &, int const &, double const &> get_A_vals(A const & a)
{
return std::forward_as_tuple(a.ch,a.i,a.d);
}
/* 2) Provide yourself with a means of applying some operation, `Func`,
to each element of an `std::tuple`
*/
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const &, Func) {}
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const & tpl, Func func)
{
func(std::get<I>(tpl));
for_each_in_tuple<I + 1>(tpl,func);
}
/* 3) Combine 1) and 2) to apply `Func` over the members of an `A`
*/
template<typename Func>
void for_each_in_A(A const & a, Func func)
{
for_each_in_tuple(get_A_vals(a),func);
}
// Testing...
#include <iostream>
// A specimen operation: just prints its argument
struct printer
{
template<typename T>
void operator () (T && t)
{
std::cout << t << std::endl;
}
};
int main()
{
A a{'a',1,2.0};
for_each_in_A(a,printer());
return 0;
}
// EOF
The program outputs:
a
1
2
If you have control of the structs or classes over whose members you need to
iterate, you may consider whether it is practical simply to dispense with them
and use the corresponding std::tuples everywhere.
Code built with gcc 4.8.2 and clang 3.3, -std=c++11.