Макрос / метапрограмма C ++ для определения количества членов во время компиляции - PullRequest
4 голосов
/ 27 июля 2011

Я работаю над приложением с основанной на сообщениях / асинхронной агентоподобной архитектурой. Будет несколько десятков различных типов сообщений, каждый из которых представлен типами C ++.

class message_a
{
  long long identifier;
  double some_value;
  class something_else;
  ...//many more data members
}

Можно ли написать макрос / метапрограмму, которая позволила бы вычислить количество членов данных в классе во время компиляции?

// например:

class message_b
{
  long long identifier;
  char foobar;
}


bitset<message_b::count_members> thebits;

Я не знаком с метапрограммированием на C ++, но могу ли я повысить :: mpl :: vector, чтобы я смог выполнить этот тип расчета?

Ответы [ 5 ]

4 голосов
/ 28 июля 2011

, как уже предлагали другие, вам нужно Boost.Fusion и его BOOST_FUSION_DEFINE_STRUCT .Вы должны будете определить свою структуру один раз, используя неиспользуемый, но простой синтаксис.В результате вы получите требуемый count_members (обычно именуемый size) и гораздо большую гибкость, чем просто.

Ваши примеры:

Определение:

BOOST_FUSION_DEFINE_STRUCT(
    (), message_a,
    (long long, identifier),
    (double, some_value)
)

использование:

message_a a;
size_t count_members = message_a::size;
3 голосов
/ 27 июля 2011

Нет, в C ++ нет способа узнать имена всех членов или сколько членов на самом деле там.

Вы можете хранить все типы в mpl::vector в своих классах, но тогда вы сталкиваетесьпроблема в том, как превратить их в члены с соответствующими именами (чего нельзя достичь без некоторого взлома макросов).

Использование std::tuple вместо POD - это решение, которое обычно работает, но создает невероятно грязный код, когда выфактически работает с кортежем (без именованных переменных), если вы не преобразуете его в какой-то момент или у вас нет оболочки, которая перенаправляет методы доступа на член кортежа.

class message {
public:
  // ctors
  const int& foo() const { return std::get<0>(data); }
  // continue boiler plate with const overloads etc

  static std::size_t nun_members() { return std::tuple_size<data>::value; }
private:
  std::tuple<int, long long, foo> data;
};

Решение с Boost.PP и MPL:

#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>
#include <boost/preprocessor.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>

struct Foo {
  typedef boost::mpl::vector<int, double, long long> types;

// corresponding type names here
#define SEQ (foo)(bar)(baz)
#define MACRO(r, data, i, elem) boost::mpl::at< types, boost::mpl::int_<i> >::type elem;
BOOST_PP_SEQ_FOR_EACH_I(MACRO, 0, SEQ)

};

int main() {
  Foo a;
  a.foo;
}

Я не проверял, чтобы не было ошибок.

0 голосов
/ 22 августа 2017

Есть несколько ответов, в которых просто говорится, что это невозможно, и если бы вы не связались с magic_get, я бы с ними согласился. Но magic_get показывает, к моему изумлению, что в некоторых случаях это действительно возможно. Это говорит о том, что доказать, что что-то невозможно, сложнее, чем доказать, что что-то возможно!

Короткий ответ на ваш вопрос - использовать средства в magic_get напрямую, а не переопределять их самостоятельно. В конце концов, даже если взглянуть на версию кода pre-Boost , не совсем понятно, как он работает. В одном месте в комментариях упоминается кое-что об аргументах конструктора; Я подозреваю, что это ключ, потому что можно посчитать аргументы для обычной функции , поэтому, возможно, он считает количество аргументов, необходимое для инициализации структуры скобками. Это указывает на то, что это возможно только с простыми старыми структурами, а не объектами с вашими собственными методами.

Несмотря на все это, я бы предложил использовать библиотеку отражений, как предлагали другие. Хорошая библиотека, которую я часто рекомендую - это библиотека protobuf от Google, в которой есть рефлексия и сериализация, а также многоязычная поддержка. Однако он предназначен только для объектов, предназначенных только для данных (например, простые старые структуры, но с векторами и строками).

0 голосов
/ 27 июля 2011

Нечто подобное может приблизить вас:

struct Foo {
    Foo() : a(boost::get<0>(values)), b(boost::get<1>(values)) {}
    int &a;
    float &b;
    typedef boost::tuple<int,float> values_t;
    values_t values;
};
0 голосов
/ 27 июля 2011

Простые структуры не поддерживают подсчет членов, но boost :: fusion предлагает хороший способ объявить структуру, которая является итеративной.

...