Knowing data type at run time

Hello,

I plan to use C++ to create Json objects i.e. name:value pairs. The name is always std::string. However, the value can be anything int, float, std::string, bool etc. But this is known only at runtime.
For ex: {"xyz": 123, "abc":"mmmm", "pqr":12.24}

How will I be able to handle this in C++?Can I use templates ?
closed account (zwA4jE8b)
1
2
3
4
5
6
template <class Type>
struct Json
{
    string Name;
    Type x, y, y;
};



something like that maybe.

to declare

Json newstructure<int>;

you can put whatever datatype identifier between the <>... i.e. <char>, <double>
Last edited on
You can use templates to facilitate the solution, but templates by themselves primarily eliminate duplication of effort and increase interoperation. They are all about compile-time. Instead, you will probably end up using boost variant/any and/or polymorphism, but the strategy you choose will depend on other factors.

How many types do you plan to support? Do you plan to continue adding new types in the future, or just have some permanent small set?

What operations do you want to support? To store and extract data or also to compare, perform arithmetic, etc.

And do you plan to support operations that mix different types? Comparing integer and floating point for example. How do you plan to do that - promoting the less expressive to the more expressive type or specializing the operation for this mixture?

I guess your question may be very specific. I approach it from the generic OOP perspective.

Regards
Thanks for this info. However, I plan to send a list of name:value pairs to create the Json object for which I will use a C++ list. Will I be able to use it with this template class?
I tried this but it wont compile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <list>

using namespace std;

template <class T>
class NameValuePair
{
    public:
    std::string name;
    T Value;
};

int main()
{
    NameValuePair<int> n;
    list<NameValuePair> ll;
    return 0;
}


Thanks,
Currently I plan to support int, float, bool and strings. As of now, I am planning for only this much and not support anything else. I would be using a Json parser like JsonCPP to create the entire Json object and it would handle the read, write, compare and other operations. My concern is being able to pass int, float, bool and strings as values in a single list of <key, value> and then process them.
list<NameValuePair> ll;
A template is type that is parametrized by another type at compile time. list is a template. I have the feeling that you expect that if instead of specific type you supply template type to list, it will automatically store run-time type information that chooses the arbitrary parameter for each element. Unfortunately nope. There are alternatives with polymorphism or the boost library, but looking at http://jsoncpp.sourceforge.net/class_json_1_1_value.html it seems that JSON itself supports the entire shebang. Or do you need something customized?

Regards
I want to write up something that does not depend on Json. Tomorrow I should be able to move to say XML and not change the upper layer.
Maybe make a wrapper class for the types you need with a common base class. Then you can use something like a hash map to store the pair.
Ok. I am not sure if this will actually make porting to another format easier, but I am not sure that it wont :) As naraku9333 said, you will need some kind of wrapper. Here is the code to illustrate one possible solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <memory>
#include <list>
#include <algorithm>
#include <functional>
#include <string>
#include <typeinfo>

using namespace std;

template <typename T>
void serializer(const T& data) {
  cout << data << " : " << typeid(T).name() << endl;
}

struct Base {
  virtual void serialize() = 0;
  virtual Base* clone() = 0;
  virtual ~Base() {};
};

template <typename T>
struct Derived : Base {

  Derived(const T& data) : data_(data) { }

  virtual void serialize() { serializer(data_); }
  virtual Derived* clone() { return new Derived(data_); }

  T data_;
};

struct Wrapper {

  template <typename T>
  Wrapper(const T& data) : impl_(new Derived<T>(data)) { }

  Wrapper(const Wrapper& src) : impl_(src.impl_->clone()) { }

  ~Wrapper() { delete impl_; }

  const Wrapper& operator= (const Wrapper& rhs)
  {
    auto_ptr<Base> tmp(rhs.impl_->clone());
    delete impl_;
    //impl_ = tmp.get();
    impl_ = tmp.release();
    return rhs;
  }

  void serialize() { impl_->serialize(); }

  Base* impl_;
};

int main()
{
  list<Wrapper> l;

  l.push_back(Wrapper(5));
  l.push_back(Wrapper(5.0));
  l.push_back(Wrapper(string("five")));

  for_each(l.begin(), l.end(), mem_fun_ref(&Wrapper::serialize));
}

Currently it doesn't handle arrays, and that's why I used the string class (; not that there is anything wrong with it.) Also, I am not sure if some of the stuff in not redundant. (I used structs just to save space from typing access specifiers.)

Regards

EDIT: The Wrapper class may be overkill. You can make list of const Base* or smth. The difference between using and not using Wrapper is that the Wrapper provides value semantics and automatic deallocation. However copying pointers is faster. Value semantics are not that important in your case, and the clone method is then also unnecessary.

EDIT 2: Error in the assignment operator of Wrapper! I used get instead of release. I'm getting sloppier by the minute.

EDIT 3: Oh, tsk tsk. Found another issue... there was no virtual destructor in Base (and there should be, because I call delete on derivatives of Base polymorphically.)
Last edited on
Have you looked at the Boost Any class?
[edit] http://www.boost.org/doc/libs/release/libs/any
Last edited on
If the number of types is small use boost::variant. Otherwise boost::any.

1
2
3
4
5
6
7
8
typedef boost::variant< int, float, bool, std::string > value_type;
typedef std::map< std::string, value_type >key_value_pairs;

key_value_pairs data;
data.insert( std::make_pair( "jsmith", value_type( 42 ) ) );
data.insert( std::make_pair( "hello", value_type( 3.14f ) ) );
data.insert( std::make_pair( "world", value_type( false ) ) );
data.insert( std::make_pair( "foo", value_type( "bar" ) ) );

boost::any is wrapper for arbitrary type, which provides value-semantics and lifetime management, just like my Wrapper class. And it would be preferable (because it is better tested as part of boost). But I think that it provides no visitation interface and no polymorphism. If that is so, how can it be used to solve the problem? I think that the OP needs something that can redirect the serialization of objects to Json, preferably in generic manner, because otherwise there will be some bulk of casting code that has to be maintained.

boost::variant is ok, but I find that it tries too hard to keep its storage embedded, like union type. The result is that it has weaker response in the presence of constructor exceptions, does cause more constructor invocations (both of which do not matter in this particular case) and still doesn't avoid dynamic allocation completely. As I understand, the initial versions used placement construction on top of another object, later versions used double storage in order to avoid the placement construction, the current version uses backup allocation on the heap. Anyway, it provides visitation mechanism, that can be used with generic visitor. And the code becomes shorter and less error-prone:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <list>
#include <algorithm>
#include <string>
#include <typeinfo>
#include <boost/variant.hpp>

using namespace std;
using namespace boost;

struct Serializer : public static_visitor<>
{
  template <typename T>
  void operator()(const T& data) const {
    cout << data << " : " << typeid(T).name() << endl;
  }
};

typedef variant<int, double, string> var_t;

int main()
{
  list<var_t> l;

  l.push_back(5);
  l.push_back(5.0);
  l.push_back(string("five"));

  struct Processor {
    static void process(var_t& data)
    {
      apply_visitor(Serializer(), data);
    }
  };


  for_each(l.begin(), l.end(), Processor::process);
}

(After encountering boost::variant recently, I find it to be somewhat of an acquired taste. My [edit]Wrapper in the previous post imposes no restriction on the types that it handles, just as long as they can be serialized. But unfortunately it is not tested so well so I can not recommend it wholeheartedly.)

Regards
Last edited on
Topic archived. No new replies allowed.