Присвоить тип переменной и передать ее шаблону

#c #templates

#c #шаблоны

Вопрос:

У меня есть следующий код

 template <typename T>
auto CallMe(int param) -> void {
    ...
}

auto Test(int userInput) -> void {
    int paramToUse = SomeFunction(userInput);
    if (userInput == 0) {
        CallMe<__int32>(paramToUse);
    } else if (userInput == 1) {
        CallMe<__int16>(paramToUse);
    } else if (userInput == 2) {
        CallMe<__int8>(paramToUse);
    } else if (userInput == 3) {
    ...
}
 

Мой вопрос в том, что, поскольку все вызовы в CallMe() точности одинаковы, кроме типа шаблона T , есть ли в C способ присвоить тип переменной в каждой условной ветви и передать ее CallMe() в одном месте, например

     typename T;

    if (userInput == 0) {
        T = __int32;
    } else if (userInput == 1) {
        T = __int16;
    } else if (userInput == 2) {
        T = __int8;
    } else if (userInput == 3) {
    ...

    CallMe<T>(paramToUse);
 

Таким образом, если подпись CallMe изменена, для обновления требуется только одна строка.

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

1. Какова цель конечного возвращаемого типа в auto CallMe(int param) -> void ?

2. Нет цели. Просто мой стиль кодирования. Я предпочитаю указывать все типы, чтобы мне не приходилось угадывать при чтении.

3. Не void CallMe(int param) сделал бы то же самое?

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

5. __int32 выглядит странно

Ответ №1:

Как насчет:

 decltype(CallMe<__int32>)* callable; // function pointer 

// set the function address as necessary 
if (userInput == 0) {
  callable = CallMe<__int32>;
} else if (userInput == 1) {
  callable = CallMe<__int16>;
} else if (userInput == 2) {
  callable = CallMe<__int8>;
} else {
 // ...
} 

callable(paramToUse); // call it
 

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

1. ? Ваш код — это точно мой первый код. И эти условия предназначены только для демонстрации.

2. @CrendKing Я этого не заметил. Смотрите обновленную версию

3. Есть ли какое-либо значение __int32 в первой строке, или это просто обязательный заполнитель?

4. @CrendKing просто заполнитель. Подойдет любой тип.

Ответ №2:

Время выполнения — условные псевдонимы типов невозможны, поскольку C является статически типизированным языком; это означает, что все типы должны быть разрешены во время компиляции.

В зависимости от вашего сценария вы можете найти решение, используя std::variant , однако вам придется либо передать новую переменную в вашу CallMe функцию, либо заменить тип paramToUse на variant .

Редактировать:

 using VariantType = std::variant<std::monostate, std::uint8_t, std::uint16_t, std::uint32_t>;
void CallMe(const VariantType value) {
    if (const auto uint8Value = std::get_if<std::uint8_t>(amp;value))
        // use uint8Value in a meaningful way
    else if (const auto uint16Value = std::get_if<std::uint16_t>(amp;value))
        // ...
    
    // ...
}

void function(const int userInput) {
    const auto result = SomeFunction(userInput);
    
    VariantType value {};
    if (result == 0)
        value = static_cast<std::uint32_t>(123);
    else if (result == 1)
        value = static_cast<std::uint16_t>(1234);
    // more conditions
    CallMe(value);
}
 

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

1. Можете ли вы привести пример? Вы предлагаете изменить CallMe его на обычную, не шаблонную функцию и принять параметр std::variant?

2. Добавлено, надеюсь, это поможет!

3. Я понимаю. По сути, вы заменяете шаблон переменной std::variant. Умный. К сожалению, real CallMe нуждается в этом T в качестве шаблона для выполнения некоторых if constexpr трюков.

4. Можете ли вы объяснить, зачем именно вам нужен этот тип? Например, если это для вызова базовой функции (например, вызова внешнего кода), этого можно избежать.

5. Обратите внимание, что все CallMe<T> они имеют один и тот же тип, что означает, что возможно сопоставление между временем выполнения userInput и временем компиляции CallMe<T> . Хотя ваш ответ, безусловно, является хорошим решением исходной проблемы OPs

Ответ №3:

поскольку все вызовы в CallMe() точности одинаковы, за исключением типа шаблона T , есть ли в C способ присвоить тип переменной в каждой условной ветви и передать ее CallMe() в одном месте

Насколько я знаю, нет.

Ну … есть using

 if (userInput == 0) {
    using T = __int32;
} else if (userInput == 1) {
    using T = __int16;
} ...

CallMe<T>(paramToUse); // T is out of scope
 

но область действия using не распространяется на вызов функции.

Плохая часть вашего примера заключается в том, что тип определяется из известного значения во время выполнения ( userInput ), поэтому я не вижу решения лучше, чем то, которое вы используете.

Отличается, если вы можете передать userInput как известное значение во время компиляции: в этом случае вы можете создать своего рода массив типов (используя std::tuple , например) и выбрать тип в вызове.

Например, предположим userInput , что значение параметра шаблона (так известно во время компиляции) и userInput значение упорядочены, начиная с нуля (как в вашем вопросе), вы можете написать

 template <std::size_t userInput>
void Test ()
 {
   using typeArray = std::tuple<__int32, __int16, __int8>;

   CallMe<std::tuple_element_t<userInput, typeArray>>(paramToUse);
 }
 

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

1. Спасибо. Я заметил using , что у него проблема с областью действия, вот почему я создал этот вопрос. Я знаю об использовании целочисленных параметров шаблона. В библиотеке векторных классов их много. Ваш пример превращается Test в шаблонную функцию, чего я не могу сделать.

2. @CrendKing — «Тест в функции шаблона, чего я не могу сделать» — поэтому я думаю, что единственный способ — это решение if-then-else-if (или также switch / case).