Showing posts with label variadic. Show all posts
Showing posts with label variadic. Show all posts

Monday, October 1, 2012

c++11 tuples and schema generation

Thanks to the new c++11 its finally possible create a system which can take in generic data structures and convert them automatically into the SQL schema and commands.

To do this you simply have to use the tuple as the class container(which you can typedef however its pleases you) and create the Database Adapter with a few of variadic templates that auto generate the SQL commands for data handling. Here is an example of generating the CREATE TABLE command for an sql-lite database adapter.

#include <iostream>
#include <string>
#include <sstream>

template <typename T>
std::string to_string(T t)
{
  //hmm this is missing!
  std::stringstream ss;
  ss << t;
  return ss.str();
}

template <typename I>
struct SchemaType
{
};

template <>
struct SchemaType<int>
{
  static constexpr const char* type = "INTEGER";
};

template <>
struct SchemaType<float>
{
  static constexpr const char* type = "REAL";
};

template <>
struct SchemaType<char*>
{
  static constexpr const char* type = "TEXT";
};

//CREATE TABLE t(x INTEGER, y, z, PRIMARY KEY(x DESC));
template <typename... II>
struct AutoSchemaCore;

template <typename I, typename... II>
struct AutoSchemaCore<I, II...>
{
  static std::string create_params()
  {
    std::string ret = "a"
      + to_string(sizeof...(II))
      + " "
      + SchemaType<I>::type;
    if (sizeof...(II) > 0)
      {
     ret += ","
   + AutoSchemaCore<II...>::create_params();
      }
    return ret;
  }
};

template <>
struct AutoSchemaCore<>
{
  static std::string create_params()
  { return ""; }
};

template <typename I, typename... II>
struct AutoSchema
{
  static std::string create_table(const char* tablename)
  {
    // CREATE TABLE t(x INTEGER, y, z, PRIMARY KEY(x DESC));
    return std::string("CREATE TABLE ") 
      + tablename
      + "("
      + AutoSchemaCore<I,II...>::create_params()
      + ");";
  }
};

int main()
{
  //generate the CREATE TABLE t(...) SQL command 
  std::cout << AutoSchema<float>::create_table("tableA") << "\n";
  std::cout << AutoSchema<int,char*,float>::create_table("tableB") << "\n";
}

Result
CREATE TABLE tableA(a0 REAL);
CREATE TABLE tableB(a2 INTEGER,a1 TEXT,a0 REAL);

Thursday, September 27, 2012

c++11 Variadic template

Another interesting feature of c++11 is the variadic templates. Basically these allow you to add infinite, heterogeneous parameters to your functions. However their syntax seems to force you to define them recursively. As a result they seem to be usable in 2 main ways.

  1. Map: Apply an repeated operation over the entire list of objects
  2. Reduce(or fold): Take in the list of objects and compound them into something
How very Hadoop. This apparent limited usability makes them a prime candidate for reduction in a forwarding function. Something that simply recurses the list of parameters and forwards them through a normaliser into a Lambda or directly into a templated function that can handle the specific Type. Here is the Normalize and Lambda approach.
#include <iostream>
#include <sstream>
#include <functional>

template <typename R, typename I>
R normalise(I i)
{
  try
    {
      R item;
      std::stringstream s;
      s << i;
      s >> item;
      return item;
    }
  catch(...)
    {}

  return R();
}

template <typename R, typename I>
R reduce(std::function<R (const R&, const R&)> action, I i)
{
  return normalise<R>(i);
}

template <typename R, typename I, typename... II>
R reduce(std::function<R (const R&, const R&)> action, I i, II... ii)
{
  return action(normalise<R>(i), reduce<R>(action, ii...));
}

template <typename R, typename I>
R map(std::function<void (const R&)> action, I i)
{
  action(normalise<R>(i));
}

template <typename R, typename I, typename... II>
void map(std::function<void (const R&)> action, I i, II... ii)
{
  action(normalise<R>(i));
  map<R>(action, ii...);
}

int main()
{ 
  std::function<int (const int&, const int&)> max
    = [](const int& a, const int& b)->int { return a>b ? a : b;  };  

  std::function<float (const float&, const float&)> min
    = [](const float& a, const float& b)->float { return a<b ? a : b;  };  

  std::function<float (const float&, const float&)> sum
    = [](const float& a, const float& b)->float { return a+b;  };  

  std::cout << "Max:" << reduce(max, "452", 3.422, 32, 0x000000ff, "543.485") << "\n";
  std::cout << "Min:" << reduce(min, "452", 3.422, 32, 0x000000ff, "543.485") << "\n";
  std::cout << "Sum:" << reduce(sum, "452", 3.422, 32, 0x000000ff, "543.485") << "\n";

  float res = 0;
  std::function<void (const float&)> accum
    = [&res](const float& a) { res += a;  };  

  map(accum, "452", 3.422, 32, 0x000000ff, "543.485");
  
  std::cout << "\n" << "Accum:" << res << "\n";

  std::stringstream ss;
  std::function<void (const float&)> stream = [&ss](const float& a) { ss << a << ",";  };

  map(stream, "452", 3.422, 32, 0x000000ff, "543.485");
  std::cout << ss.str() << "\n";
}