Как использовать шаблоны для вызова функции на основе типа данных? - PullRequest
1 голос
/ 16 апреля 2019

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

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

void printdata(uint32_t data) 
{
  Serial.printf("Printing unsigned integer %d", data);
}

void printdata(float data) 
{
  Serial.printf("Printing float %i", data);
}

template<typename Data>
void myTemplate(const Data& d)
{
  printdata(d);
}

Я бы хотел, чтобы функция шаблона вызывала printdata (uint32_t), если передается целое число (например, printdata(1000)).Я также хотел бы, чтобы функция шаблона вызывала printdata (float), когда передается float (например, printdata(10.00)).

Однако, когда я вызываю printdata(1000), мой компилятор выдает мне сообщение о том, что call of overloaded 'printdata(const double&)' is ambiguous.

Может ли это быть достигнуто без необходимости вручную все разыгрывать?Я хочу, чтобы библиотека, которую я пишу, была максимально удобной и менее подробной.Ручная отливка всего не очень хорошее решение для меня.

Спасибо!

Ответы [ 2 ]

1 голос
/ 16 апреля 2019

Рассмотрим std::is_integral из заголовка <type_traits>, а также C ++ 17 constexpr if.

std::is_integral сообщит вам, является ли данный тип целочисленным во время компиляции.

constexpr if - это в основном оператор if, который вычисляется во время компиляции. Неиспользуемая ветвь никогда не выполняется (фактически она даже не компилируется).

#include <type_traits>

void printdata(uint32_t data)
{
    Serial.printf("Printing unsigned integer %d", data);
}

void printdata(float data)
{
    Serial.printf("Printing float %i", data);
}

template<typename Data>
void myTemplate(const Data& d)
{
    // uses C++17 constexpr if
    if constexpr (std::is_integral_v<Data>) printdata(static_cast<uint32_t>(d));
    else printdata(static_cast<float>(d));
}

РЕДАКТИРОВАТЬ: Однако, если вы не можете использовать C ++ 17, вам нужно будет использовать более изящную логику шаблонов, которую я продемонстрирую позже. Я также не буду использовать что-либо из заголовка type_traits, потому что вы упомянули, что по какой-то причине у вас нет к нему доступа?

ПРИМЕЧАНИЕ: Код, который я собираюсь показать, очень грубый, и я бы никогда не рекомендовал писать его вне академических упражнений или любопытства. Причиной этого является отсутствие заголовка type_traits, что означает, что мы должны определить намного больше, чем мы могли бы иметь, например, с помощью, например. SFINAE. Кроме того, в большинстве шаблонов кода используется множество type_traits, что делает его еще более неловким.

Итак, на данный момент (без заголовка type_traits или C ++ 17), Я бы предложил не использовать шаблоны и просто делать перегрузки для каждого типа, с которым вы хотите использовать printdata, с которым устраняет проблему неоднозначных перегрузок.

// given a type T, defines a static member function called f that routes to the correct form of printdata.
// default implementation goes to int version.
template<typename T> struct _get_version { static void f(T val) { printdata(static_cast<uint32_t>(val)); } };

// specialize this for all the floating point types (float, double, and long double).
template<> struct _get_version<float> { static void f(float val) { printdata(static_cast<float>(val)); } };
template<> struct _get_version<double> { static void f(double val) { printdata(static_cast<float>(val)); } };
template<> struct _get_version<long double> { static void f(long double val) { printdata(static_cast<float>(val)); } };

template<typename Data>
void myTemplate(Data d)
{
    // get the version Data should use, then use its internal f function
    _get_version<Data>::f(d);
}
0 голосов
/ 16 апреля 2019

Иногда нам приходится предоставлять дополнительные перегрузки для устранения неоднозначности между опциями.В этом случае мы предоставили бы дополнительные перегрузки для printdata.

Лучший вариант - просто добавить дополнительные функции printdata для int s, double s и других типов данных, которые вас интересуют, но если вы хорошо печатаете их как unsigned intfloat s, вы можете просто разыграть их.

void printdata(uint32_t data) 
{
  Serial.printf("Printing unsigned integer %d", data);
}

// ints should be printed as unsigned ints?
void printdata(int data) {
  printdata((uint32_t)data); //Call unsigned version
}

void printdata(float data) 
{
  Serial.printf("Printing float %i", data);
}

//doubles should be printed as floats
void printdata(double data)
{
  printdata((float)data);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...