#include <iostream>
#include <map>
#include <memory>
#include <string>

#define CONCAT(x, y) CONCAT_(x, y)
#define CONCAT_(x, y) x ## y

#define PAR(type, name, descr) \
type name; \
const bool CONCAT(doRegistration, name) = [this]() { \
    entries.emplace(#name, \
                    std::unique_ptr<BaseEntry>( \
                        new Entry<Model, type>{ type##_type, &Model::name })); \
    return true; \
}();

enum ParType { double_type, int_type };

class Model;

struct BaseEntry
{
    BaseEntry(ParType type) : type(type) {}
    ParType type;

    template <typename C, typename T>
    T & get(C &c);
};

template <typename C, typename T>
struct Entry : BaseEntry
{
    Entry(ParType type, T C::* ptr) : BaseEntry(type), ptr(ptr) {}
    T C::* ptr;
};

template <typename C, typename T>
T & BaseEntry::get(C &c)
{
    return c.*static_cast<Entry<C, T>*>(this)->ptr;
}

class Model
{
public:
    Model() : a(1.0), b(2) {}

    std::map<std::string, std::unique_ptr<BaseEntry>> entries;

    PAR(double, a, "какой то параметр");
    PAR(int, b, "и еще один параметр");
};

int
main(void)
{
    Model m;
    for (const auto &e : m.entries) {
        std::cout << e.first << std::endl;
        switch (e.second->type) {
            case double_type:
                e.second->get<Model, double>(m) = 10.1;
                std::cout << e.second->get<Model, double>(m) << std::endl;
                break;
            case int_type:
                std::cout << e.second->get<Model, int>(m) << std::endl;
                break;
        }
    }
    return 0;
}