Генерирующая функция конкретной подписи с известным возвращаемым значением

#c #variadic-templates

#c #переменные-шаблоны

Вопрос:

Есть ли способ сгенерировать статическую функцию (указатель), которая: 1. Имеет определенную подпись. 2. Возвращает конкретное значение. 3. Игнорирует все аргументы.

Что-то вроде:

 template<typename ReturnType, ReturnType defaultValue, typename... Args>
ReturnType FallbackFunction(Args... ) {
    return defaultValue;
}

int threeParamFunction(int one, int two, int three)
{
    return one   two   three;
}

float twoParamFunction(float one, float two)
{
    return one   two;
}

int main()
{
    // This somehow works
    using ThreeParamFunction = decltype(amp;threeParamFunction);
    ThreeParamFunction fncPointerZero = FallbackFunction<int, 0>;
    cout << "Returning zero: " << fncPointerZero(5, 10, 15) << std::endl;
    ThreeParamFunction fncPointerOne = FallbackFunction<int, 1>;
    cout << "Returning one: " << fncPointerOne(5, 10, 15) << std::endl;

    // Does not compile:
    //using TwoParamFunction = decltype(amp;twoParamFunction);
    //TwoParamFunction fncPointerSeven = FallbackFunction<float, 7.0f>;
    //cout << "Returning seven: " << fncPointerSeven(5, 10) << std::endl;

    return 0;
}
  

Мотивация заключается в создании резервной функции, которая возвращает известное значение, если приложение не может загрузить соответствующую функцию.

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

1. «игнорирует»? … или std::forward s?

2. Где определена функция twoParamFunction?

3. Почему бы просто не установить указатель на функцию nullptr и не проверить, что функция не равна нулю, прежде чем вызывать ее? Кроме того, в вашем примере, что не так int threeParamFunctionBackup(int, int, int) {return some_default_value;} ?

4. @Peter В большинстве случаев вы правы. Но если у вас есть такие функции, как isSomethingEnabled, isSomethingPresent, загружаемые динамически и используемые во всем коде, вам не нужно проверять указатель перед каждым вызовом.

5. В этом случае isSomethingEnabled не следует динамически загружать. Это должна быть единственная функция, которая проверяет, была ли загружена динамически загружаемая функция перед ее вызовом. Если ваш пользовательский код ожидает вызова динамически загружаемой функции везде, ему необходимо сначала проверить эту функцию.

Ответ №1:

Вы не можете использовать адрес / тип функции шаблона (но вы можете для конкретных экземпляров).

итак, ваш

 auto f0 = amp;FallbackFunction<int, 0>; // decltype(f0) is `int (*)()` not `int (*)(Ts...)`
  

но действительно, в вашем случае

 int (*fncPointer)(int, int, int) = amp;FallbackFunction<int, 0>;
// Only FallbackFunction<int, 0, int, int, int> is valid
// it is mostly static_cast<int (*)(int, int, int)>(amp;FallbackFunction<int, 0>)
// Which force deduction to FallbackFunction<int, 0, int, int, int>.
  

Так что либо укажите все аргументы:

 auto f2 = amp;FallbackFunction<int, 0, int, int>; // decltype(f2) is `int (*)(int, int)`
  

Или вы можете создать функтор с operator() помощью (с помощью лямбда):

 auto foo = [](auto...){ return 0; };
foo(); foo(1); foo(1, 2, 3);

auto bar = [](auto...){ return 4.2f; };
bar(); bar(1); bar(1, 2, 3);
  

Кроме того, float недопустимый параметр, не относящийся к типу:

 template <float f> struct S{}; // invalid.
  

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

1. Более интересный вопрос, по-видимому, заключается в том, почему первая версия вообще работает? И чем она отличается от второго типа функции. Живой пример .

2. @super: пропустите этот момент. float недопустимый параметр, не относящийся к типу.

3. Отлично, я могу взять этот функтор и присвоить его указателю, он вычтет правильный тип. Мне просто нужен отдельный функтор для каждого возвращаемого типа.