Макросы C : условный код, включаемый в оператор expression NOT

#c #macros #boost-preprocessor #x-macros

#c #макросы #boost-препроцессор #x-макросы

Вопрос:

Хорошо, я знаю, что макросы — это зло, и их следует избегать любой ценой. Я пытаюсь сократить значительный объем кода и повторений, из-за которых будет сложно не допустить ошибок с опечатками, если я не смогу его макротизировать.

Это несколько надуманный пример, поскольку я попытался свести имеющийся у меня реальный код к более сжатому примеру.

 #include <map>
#include <iostream>
#include <functional>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/comparison/equal.hpp>

using FUNC = std::function<int(int,int)>;

#define BINDIF( method ) BOOST_PP_IF(1,std::bind(amp;B::method, this, std::placeholders::_1,std::placeholders::_2),nullptr)
//                                   ^ this needs to vary on value of the method param
//                                   ^ I tried BOOST_PP_EQUAL(nullptr, method) here
//                                     it concatenated several Boost macros and threw
//                                     an error about not recognizing the long symbol
//                                     it created

#define TABLE       
T(Add, add)         
T(Sub, sub)         
T(Mul, mul)         
T(Div, div)

#define T(k,v) k,
enum Ops: size_t {
    TABLE
};
#undef T

class A {
public:
    FUNC Func;
    A(FUNC func)
        : Func(func) {
    }
};

class B {
    std::map<Ops,A> As;
public:
    int add(int x, int y) { return x   y; }
    int sub(int x, int y) { return x - y; }
    int mul(int x, int y) { return x * y; }
    int div(int x, int y) { return x / y; }

    B() {

        #define T(k,v) std::make_pair(Ops::k, A( BINDIF(v) ) ),
        As = {
            TABLE
        };
        #undef T
    }
};

int main() {
    return 0;
}
  

Это позволит корректно развернуть код std::bind, найденный в макросе BINDIF. Однако обратите внимание, что первому параметру, условному, присвоено значение 1. Если я изменю это значение на 0, оно корректно уменьшится в значении nullptr. Проблема в том, что я хотел бы, чтобы это происходило при оценке параметра ‘method’, переданного макросу BINDIF. Я хотел бы, чтобы в таблицу были введены значения nullptr, и поэтому код std::bind не будет сгенерирован.

Я полностью осознаю, насколько надуманным кажется этот пример, но в реальном коде, с которым я работаю, большинство столбцов содержат nullptr, поскольку я редко привязываюсь к одному из 4 указателей std::function.

Вот расширение с 1:

 ><(((º> g   -E -g -O0 -Wall -std=c  11 test.cpp

...removed for brevity

class B {
    std::map<Ops,A> As;
public:
    int add(int x, int y) { return x   y; }
    int sub(int x, int y) { return x - y; }
    int mul(int x, int y) { return x * y; }
    int div(int x, int y) { return x / y; }

    B() {


        As = {
            std::make_pair(Ops::Add, A( std::bind(amp;B::add, this, std::placeholders::_1,std::placeholders::_2) ) ), 
            std::make_pair(Ops::Sub, A( std::bind(amp;B::sub, this, std::placeholders::_1,std::placeholders::_2) ) ), 
            std::make_pair(Ops::Mul, A( std::bind(amp;B::mul, this, std::placeholders::_1,std::placeholders::_2) ) ), 
            std::make_pair(Ops::Div, A( std::bind(amp;B::div, this, std::placeholders::_1,std::placeholders::_2) ) ),
        };

    }
};

int main() {
    return 0;
}
  

Вот расширение с 0:

 ><(((º> g   -E -g -O0 -Wall -std=c  11 test.cpp

...removed for brevity

class B {
    std::map<Ops,A> As;
public:
    int add(int x, int y) { return x   y; }
    int sub(int x, int y) { return x - y; }
    int mul(int x, int y) { return x * y; }
    int div(int x, int y) { return x / y; }

    B() {


        As = {
            std::make_pair(Ops::Add, A( nullptr ) ), 
            std::make_pair(Ops::Sub, A( nullptr ) ), 
            std::make_pair(Ops::Mul, A( nullptr ) ), 
            std::make_pair(Ops::Div, A( nullptr ) ),
        };

    }
};

int main() {
    return 0;
}
  

Пожалуйста, извините за любые опечатки и общую длину этого вопроса. Последние несколько дней я искал ответ здесь и в других местах и не мог найти ничего, что, казалось, работало. Документация по макросам Boost довольно скудна.

Смехотворно, конечно. Таблица с nullptr, подобная этой:

 #define TABLE       
T(Add, add)         
T(Sub, sub)         
T(Mul, mul)         
T(Div, nullptr)
  

В результате получился бы код, подобный этому:

 As = {
    std::make_pair(Ops::Add, A( std::bind(amp;B::add, this, std::placeholders::_1,std::placeholders::_2) ) ), 
    std::make_pair(Ops::Sub, A( std::bind(amp;B::sub, this, std::placeholders::_1,std::placeholders::_2) ) ), 
    std::make_pair(Ops::Mul, A( std::bind(amp;B::mul, this, std::placeholders::_1,std::placeholders::_2) ) ),
    std::make_pair(Ops::Div, A( nullptr ) ),
};
  

Комментарии:

1. Для тех, у кого мало внимания, не могли бы вы, возможно, добавить очень простой, короткий псевдопример того, какой конечный синтаксис вы хотели бы использовать и что он должен делать?

2. Я добавил дополнение, показывающее, как таблица повлияет на результирующий код…

3. Я пытался использовать BOOST_PP_EQUAL(nullptr, метод) вместо 0 или 1, но он объединяет кучу макросов Boost, а затем сообщает, что они не существуют.

4. Если Add , Sub и т.д. не обязательно должны быть функциями-членами, вы можете избежать их реализации самостоятельно, используя std::plus , std::minus и т.д. Если они действительно должны быть функциями-членами, по крайней мере, объявите их const . Вероятно, вы можете воспользоваться списками инициализаторов и заменить назначение чем-то вроде As = std::map<Ops, A>{ { Op1, A() }, { Op2, A() } };

5. @CaptainObvlious да, это не реальный пример, просто что-то, что простые читатели смогут понять.

Ответ №1:

Хорошо, я думаю, что эта проблема неразрешима с помощью макросов. Я думаю, что на этом этапе потребуется мощь метапрограммирования шаблона. Для всех, кому интересно, мне нужно во время компиляции либо передать nullptr, либо привязать к методу класса. Поиск продолжается…