Are there alternatives for reflection/introspection in C++?

Asked

Viewed 467 times

18

I have the following problem.

Given some kind of a guy T:

template <typename T>
  1. I need to be able to convert an object like T on a map std::unordered_map<std::string, boost::any> containing an entry for each attribute. I initially think of "flat" objects, only with primitive attributes and strings.

    std::unordered_map<boost::any, boost::any>& asUnordered_map(const T& obj)
    
  2. I also need to do the opposite, that is, given a std::map<std::string, boost::any> and an object T i want to update the object attributes:

    T& asObject(const std::unordered_map<boost::any, boost::any>& map)
    

In Java a natural implementation would use Introspector and techniques of reflection.

It seems however that C++ still does not support reflection by default (the committee and certain study groups are advancing on this front for a next version) and emulating this functionality externally takes some work (see CPP-Reflection). That way I would like to know if there is a more idiomatic alternative to solve this type of problem.

How can I find out what are the attributes of an object and how can I recover / modify values of an unknown type in C++?

  • 3

    You have to come up with some complex solution of your own. Some may only be library-based, but to get Steady" you really need external tools like Qt: http://doc.qt.io/qt-4.8/moc.html

  • 1

    Javeiro in C++ is a danger... Is there an idiomatic alternative? The problems are those of always, serialization and "deserialization" (I don’t know if this is a word) of generic textual formats.

  • I don’t have a C++ domain and I don’t know any simple "pretty" solutions. To tell you the truth, the MOC is one of the things that makes me not like Qt so much. The alternative you already think is a good option. I don’t want to answer because it would be more my opinion than something I actually know.

  • 2

    If "the problems are always those, serialization and deserialization", why don’t you use the Serialization of Boost itself? You’ll have to do the mashelling of each class, true, but then Object Orientation helps you (reusing the serialization of the attributes of the (s) base class(s)).

  • Or, there’s also the Ceral. :)

  • Hi @Luiz, from what I saw both solutions still require you to implement some method telling you exactly which fields should be serialized in each type of class (what I’m trying to avoid). Also the serialization format is a pretty boring XML and I already have a solution ready to persist / read something with a unordered_map. It’s not the end of the world to have to rewrite that part if I have to, but I wish I didn’t have to implement a serialization method for each object if it’s possible.

  • 2

    Unfortunately, there’s nothing currently doing that. There are library solutions, but they all need you to write down the types that you want to intrude on. An example library that allows this is: http://www.boost.org/doc/libs/1_61_0/libs/hana/doc/html/index.html#tutorial-introspection-adapting

  • 2

    If you’re still thinking of creating your own solution, it’s worth watching C++14 Reflections Without Macros, Markup nor External Tooling..

  • 1

    There is a reminder that Java is written in C++ follow the tutorial link on how to do : http://jackieokay.com/2017/04/13/reflection1.html

  • 1

    "Every sufficiently complex C program ends up having a bugada implementation, improvised, poorly specified and slow half of Lisp" (Greenspun’s tenth rule)

Show 6 more comments

1 answer

1

You can use Boost Hana?

https://github.com/boostorg/hana

You will not have Reflection, but will work with Meta True Programming.

#include <boost/hana.hpp>
#include <cassert>
#include <string>
namespace hana = boost::hana;
using namespace hana::literals;

struct Fish { std::string name; };
struct Cat  { std::string name; };
struct Dog  { std::string name; };

int main() {
  // Sequences capable of holding heterogeneous objects, and algorithms
  // to manipulate them.
  auto animals = hana::make_tuple(Fish{"Nemo"}, Cat{"Garfield"}, Dog{"Snoopy"});
  auto names = hana::transform(animals, [](auto a) {
    return a.name;
  });
  assert(hana::reverse(names) == hana::make_tuple("Snoopy", "Garfield", "Nemo"));

  // No compile-time information is lost: even if `animals` can't be a
  // constant expression because it contains strings, its length is constexpr.
  static_assert(hana::length(animals) == 3u, "");

  // Computations on types can be performed with the same syntax as that of
  // normal C++. Believe it or not, everything is done at compile-time.
  auto animal_types = hana::make_tuple(hana::type_c<Fish*>, hana::type_c<Cat&>, hana::type_c<Dog*>);
  auto animal_ptrs = hana::filter(animal_types, [](auto a) {
    return hana::traits::is_pointer(a);
  });
  static_assert(animal_ptrs == hana::make_tuple(hana::type_c<Fish*>, hana::type_c<Dog*>), "");

  // And many other goodies to make your life easier, including:
  // 1. Access to elements in a tuple with a sane syntax.
  static_assert(animal_ptrs[0_c] == hana::type_c<Fish*>, "");
  static_assert(animal_ptrs[1_c] == hana::type_c<Dog*>, "");

  // 2. Unroll loops at compile-time without hassle.
  std::string s;
  hana::int_c<10>.times([&]{ s += "x"; });
  // equivalent to s += "x"; s += "x"; ... s += "x";

  // 3. Easily check whether an expression is valid.
  //    This is usually achieved with complex SFINAE-based tricks.
  auto has_name = hana::is_valid([](auto&& x) -> decltype((void)x.name) { });
  static_assert(has_name(animals[0_c]), "");
  static_assert(!has_name(1), "");
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.