Заставить макрос выдать ошибку времени компиляции для несуществующей функции

#c #c-preprocessor

#c #c-препроцессор

Вопрос:

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

Я хотел бы создать макрос, который выдает лямбда-выражение для сопоставления объектов, созданных с помощью вызова определенной функции. В дополнение к этому макрос также должен приводить к ошибке времени компиляции, если функция с именем, указанным в качестве параметра, не существует в пространстве имен. Влияние на результаты в случае успешной компиляции? (Если это вообще возможно, результаты компиляции с этой проверкой и без нее будут одинаковыми, не полагаясь на код, который может быть или не быть оптимизирован в зависимости от уровня оптимизации.) Возможно ли это? Если это возможно: как я могу это реализовать?

Пример того, чего я пытаюсь достичь; макрос, о котором я спрашиваю, это CALL_INFO_PREDICATE ; static_assert_function_exists часть является заполнителем для любого кода, который позволит достичь желаемого результата:

 struct CallInfo
{
    const std::string m_function;
    CallInfo(const std::stringamp; function)
        : m_function(function)
    {
    }
}

std::vector<CallInfo*> g_calledFunctions;

namespace mymath
{

    // all functions with same return type, but differnent parameter lists

    double sin(double radian)
    {
        g_calledFunctions.push_back(new CallInfo("sin"));
        ...
    }

    double atan2(double y, double x)
    {
        g_calledFunctions.push_back(new CallInfo("atan2"));
        ...
    }
    
}

#define CALL_INFO_PREDICATE(functionName) [] (CallInfo* info) -> bool
{
    static_assert_function_exists(mymath::functionName);/* << how to make this work? */
    return info->m_function == #functionName;
}

int main ()
{
    mymath::sin(1);
    mymath::atan2(3, 7);

    auto pos = std::find_if(g_calledFunctions.begin(), g_calledFunctions.end(), CALL_INFO_PREDICATE(sin)); // compiles; function sin exists in mymath

    auto pos2 = std::find_if(g_calledFunctions.begin(), g_calledFunctions.end(), CALL_INFO_PREDICATE(cos)); // produces compile time error, since there is no function named cos in mymath

    ...
}
  

Ответ №1:

Почему бы не сохранить указатели на функции в CallInfo и устранить необходимость в макросах?

 struct CallInfo
{
    const std::string m_function;
    void* m_function_ptr;
    CallInfo(const std::stringamp; function, void* function_ptr)
        : m_function(function),
          m_function_ptr(function_ptr)
    {
    }
}

std::vector<CallInfo*> g_calledFunctions;

namespace mymath
{

    // all functions with same return type, but differnent parameter lists

    double sin(double radian)
    {
        g_calledFunctions.push_back(new CallInfo("sin", sin));
        ...
    }

    double atan2(double y, double x)
    {
        g_calledFunctions.push_back(new CallInfo("atan2", atan2));
        ...
    }
    
}

int main ()
{
    mymath::sin(1);
    mymath::atan2(3, 7);

    auto pos = std::find_if(g_calledFunctions.begin(), g_calledFunctions.end(), [](CallInfo* info) { return info->m_function_ptr == amp;mymath::sin; });

    auto pos2 = std::find_if(g_calledFunctions.begin(), g_calledFunctions.end(), [](CallInfo* info) { return info->m_function_ptr == amp;mymath::cos; });

    // ...
}
  

Теперь компилятор может автоматически проверить, существует ли функция.

Ответ №2:

Вы можете просто использовать его, предполагая отсутствие перегрузок:

 #define CALL_INFO_PREDICATE(functionName) [] (CallInfo* info) -> bool 
{ 
    static_cast<void>(amp;mymath::functionName); 
    return info->m_function == #functionName; 
}
  

приведение к void , чтобы избежать предупреждения о неиспользуемом выражении.

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

1. Вы случайно не знаете, гарантированно ли C оптимизирует это или это просто очень вероятно в соответствии со стандартом? (Кстати: извините, что я не буду предоставлять обратную связь в течение некоторого времени. Не могу проверить это сразу, но это выглядит многообещающе.)

2. Получение адреса не выполняется.

3. @fabian amp;math::functionName по сути является постоянным значением. Это не может быть преобразовано в код, даже если компилятор попытается.