Нужна магия метапрограммирования, чтобы безошибочно определить источник битовых полей - PullRequest
3 голосов
/ 30 июня 2010

Цель состоит в том, чтобы контролировать, каким типам пользователей разрешено выполнять какие операции на уровне пользовательского интерфейса.Этот код был на месте некоторое время;Я просто хочу немного улучшить это.Файл, который я пытаюсь улучшить, вероятно, должен быть сгенерирован автоматически, но это было бы слишком большим изменением, поэтому я ищу более простое решение.

Файл, который мы назовем PermissionBits.h, имеет кучуиз них:

// Here names are mangled; for example XYZ_OP_A is:
// permission to operation A in category/context XYZ
// SCU64 = static const unsigned __int64
// Some namespaces utilize all 64 bits
// The actual values (as long as they are proper bit fields) 
// do not matter - they are always used by name
namespace XYZPermissionBits
{
    SCU64 XYZ_OP_A = 1UI64 <<  0; //    1 = 0x0000000000000001
    SCU64 XYZ_OP_B = 1UI64 <<  1; //    2 = 0x0000000000000002
    SCU64 XYZ_OP_C = 1UI64 <<  2; //    4 = 0x0000000000000004
    SCU64 XYZ_OP_C = 1UI64 <<  3; //    8 = 0x0000000000000008
    SCU64 XYZ_OP_D = 1UI64 <<  4; //   16 = 0x0000000000000010
    SCU64 XYZ_OP_E = 1UI64 <<  5; //   32 = 0x0000000000000020
    SCU64 XYZ_OP_F = 1UI64 <<  6; //   64 = 0x0000000000000040
    SCU64 XYZ_OP_G = 1UI64 <<  7; //  128 = 0x0000000000000080
    SCU64 XYZ_OP_H = 1UI64 <<  8; //  256 = 0x0000000000000100
    SCU64 XYZ_OP_I = 1UI64 <<  9; //  512 = 0x0000000000000200
    SCU64 XYZ_OP_J = 1UI64 << 10; // 1024 = 0x0000000000000400
    SCU64 XYZ_OP_K = 1UI64 << 11; // 2048 = 0x0000000000000800
    SCU64 XYZ_OP_L = 1UI64 << 12; // 4096 = 0x0000000000001000 
}

Даже с помощью ярлыка 1UI64 << <numBits>; все еще есть проблемы, так как кодеры создают флаги с дублирующимися значениями, делают опечатки и т. д.

В идеале я хотел бымакрос, который может быть красиво отформатирован и выглядеть следующим образом:

BITFIELDS_FOR_NAMESPACE(
    //*************** <<== I want to make the namespace name more vivid
    XYZPermissionBits,
    //*************** <<== somehow if that is possible. It is not a must-have.
    XYZ_OP_A, // Being able to add a comment here would be nice, but not critical
    XYZ_OP_B,
    XYZ_OP_C,
    XYZ_OP_D,
    XYZ_OP_E,
    XYZ_OP_F,
    XYZ_OP_G,
    XYZ_OP_H,
    XYZ_OP_I,
    XYZ_OP_J,
    XYZ_OP_K,
    XYZ_OP_L
)

Я бы хотел, чтобы этот макрос был гибким и не позволял мне вводить менее 2 или более 65 аргументов - имя пространства имен + 64 флага.Можно ли делать то, что я хочу, или близко к этому, или я должен прибегнуть к сгенерированному коду?Какой еще совет ты имеешь?

Ответы [ 5 ]

6 голосов
/ 30 июня 2010

Протестированный пример с использованием Boost.PreProcessor:

#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/comparison/greater.hpp>
#include <boost/preprocessor/comparison/less.hpp>
#include <boost/preprocessor/debug/assert.hpp>
#include <boost/preprocessor/seq/size.hpp>

#define CHECK_SIZE(size) \
  BOOST_PP_ASSERT_MSG(BOOST_PP_GREATER(size, 1), "<  2 :(") \
  BOOST_PP_ASSERT_MSG(BOOST_PP_LESS(size, 65),   "> 64 :(") \

#define DO_MAKE_BITFIELDS(a, b, i, elem) \
  SCU64 elem = 1UI64 << i;

#define BITFIELDS_FOR_NAMESPACE(name, seq) \
  CHECK_SIZE(BOOST_PP_SEQ_SIZE(seq)) \
  namespace name { \
    BOOST_PP_SEQ_FOR_EACH_I(DO_MAKE_BITFIELDS, _, seq) \
  }

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

BITFIELDS_FOR_NAMESPACE(
    XYZPermissionBits,
    (XYZ_OP_A)
    (XYZ_OP_B)
    // ...
);
2 голосов
/ 30 июня 2010

Если вы решите пойти по пути генерации кода, тогда я предлагаю взглянуть на Cog .

Cog позволяет вставлять код Python в виде комментариев в исходный файл C ++ (или любого другого языка), а при запуске через Cog вывод Python вставляется в виде исходного кода, поэтому код генератора и сгенерированный вывод управляются в одном и том же файл. Это делает обслуживание простым. и код Python документирует, как был создан сгенерированный код.

Вот ваш пример использования cog, включая сгенерированный код:

namespace XYZPermissionBits
{
    /* [[[cog
    import cog
    operations = ["A", "B", "C", "D", 
                  "E", "F", "G", "H",
                  "I", "J", "K", "L"]

    assert 2 <= len(operations) <= 64

    for idx,op in enumerate(operations):
        cog.outl("SCU64 XYZ_OP_%s = 1UI64 << %s;" % (op, idx))

    ]]] */
    SCU64 XYZ_OP_A = 1UI64 << 0;
    SCU64 XYZ_OP_B = 1UI64 << 1;
    SCU64 XYZ_OP_C = 1UI64 << 2;
    SCU64 XYZ_OP_D = 1UI64 << 3;
    SCU64 XYZ_OP_E = 1UI64 << 4;
    SCU64 XYZ_OP_F = 1UI64 << 5;
    SCU64 XYZ_OP_G = 1UI64 << 6;
    SCU64 XYZ_OP_H = 1UI64 << 7;
    SCU64 XYZ_OP_I = 1UI64 << 8;
    SCU64 XYZ_OP_J = 1UI64 << 9;
    SCU64 XYZ_OP_K = 1UI64 << 10;
    SCU64 XYZ_OP_L = 1UI64 << 11;
// [[[end]]]
}
2 голосов
/ 30 июня 2010

IFAIK, библиотека препроцессора boost http://www.boost.org/doc/libs/1_43_0/libs/preprocessor/doc/index.html содержит все необходимые вам примитивы.

0 голосов
/ 30 июня 2010

Я сделал что-то вроде этого.Создайте макрос с именем MAKE_THINGS, который выглядит следующим образом:

#define MAKE_THINGS \
  MAKE_THING(NAME1) \
  MAKE_THING(NAME2) \
  MAKE_THING(NAME3) \
  /* Include semantically-blank line after list */

Затем можно определить MAKE_THING, запустить объявление, вызвать MAKE_THINGS, завершить объявление и отменить определение MAKE_THING.При желании каждая вещь может включать более одного атрибута:

Например:

#define MAKE_THINGS \
  MAKE_THING(HAPPY,"Happy") \
  MAKE_THING(SAD_AND_BLUE,"Sad and blue") \
  MAKE_THING(SLEEPY,"Sleepy") \
  /* Include semantically-blank line after list */

#define MAKE_THING(x,y) NUMBER_##x,
typedef enum {MAKE_THINGS LAST_THING} THING_NUMBER;
#undef MAKE_THING

#define MAKE_THING(x,y) FLAG_##x = (1L << NUMBER##x),
typedef enum {MAKE_THINGS ALL_FLAGS = (1 << LAST_THING)-1} THING_FLAGS;
#undef MAKE_THING

#define MAKE_THING(x,y) const char *MSG_##x = y;
MAKE_THINGS
#undef MAKE_THING

#define MAKE_THING(x,y) MSG_##x,
const char *thing_names[] = {MAKE_THINGS 0};
#undef MAKE_THING

Обратите внимание, что все объявления автоматически сохраняются параллельными.Каждая вещь получает номер (0..N-1), флаг (бит, соответствующий ее номеру), строку сообщения и место в массиве строк сообщения.

0 голосов
/ 30 июня 2010

Вместо макросов / препроцессоров / и т. Д. Я бы использовал статический текстовый файл, похожий на оригинальный. Кажется, это намного легче понять, расширить или поддержать. Я мог бы определить каждое значение как сдвиг предыдущего значения, но некоторым читателям это может показаться более запутанным. Чтобы помочь пользователям избежать опечаток, я бы, вероятно, использовал более подробное сокращение для каждого «op» вместо A, B, C (определяется дважды), D ...

...