0

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 ;-)

HKTonyLee
  • 3,111
  • 23
  • 34

3 Answers3

1

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

R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • I am still digesting your answer :-/ It is really hard to understand LOL. One more question: How come the `...` can exist in statement `return { get( row, Is+1 )... };`. I read several sites but no one talk about that. – HKTonyLee Aug 25 '14 at 10:08
  • @hktonylee I am constructing the `tuple` return value there using `return {}` syntax. – Yakk - Adam Nevraumont Aug 25 '14 at 10:41
0
  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.

  2. 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...);
    }
    
  3. 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

Community
  • 1
  • 1
Constructor
  • 7,273
  • 2
  • 24
  • 66
  • Thank you very much... But macro is hard to understand for a C++ learner ;-) – HKTonyLee Aug 25 '14 at 11:06
  • @HKTonyLee Macro is not a key thing here. You can simply write `Get` function overloads `void Get(X& arg, const MySQLRow& row, std::size_t index) { arg = row.GetX(index); }` manually if you want. – Constructor Aug 25 '14 at 15:30