Как определить тип uint8_t в пакете параметров - PullRequest
0 голосов
/ 31 августа 2018

Примечание: следующий код был изменен на основе этого сообщения: https://stackoverflow.com/a/27375389

#include <iostream>
#include <type_traits>

template<class Head>
void print_args(std::ostream& s, Head&& head) {
    s << std::forward<Head>(head);
}

template<class Head, class... Tail>
void print_args(std::ostream& s, Head&& head, Tail&&... tail) {
    if (std::is_same<Head, uint8_t>::value)
        s << static_cast<int>(head) << ",";       // cast uint8_t so that the value of 1 or 0 can be displayed correctly in console
    else
        s << std::forward<Head>(head) << ",";
    print_args(s, std::forward<Tail>(tail)...);
}

template<class... Args>
void print_args(Args&&... args) {
    print_args(std::cout, std::forward<Args>(args)...);
}

int main()
{
    uint8_t val = 1;
    print_args(std::string("hello"), val); // error: invalid static_cast from type 'std::basic_string<char>' to type 'int'
    print_args("hello", val); // error: invalid static_cast from type 'const char [6]' to type 'int'
}

Вопрос> Мне нужно привести uint_8 к int, чтобы значение могло правильно отображаться в консоли. Тем не менее, приведенный выше код имеет проблему сборки для std::string или const char*.

Что такое исправление для функции?

Ответы [ 3 ]

0 голосов
/ 31 августа 2018

Одно из возможных решений:

#include <iostream>
#include <type_traits>

namespace detail
{
    namespace cpp11
    {
        template<class arg>
        void print(std::ostream& s, arg&& a, typename std::enable_if<std::is_same<typename std::remove_reference<typename std::remove_cv<arg>::type>::type, uint8_t>::value>::type* =0)
        {
            s << static_cast<int>(a) << ",";
        }

        template<class arg>
        void print(std::ostream& s, arg&& a, typename std::enable_if<!std::is_same<typename std::remove_reference<typename std::remove_cv<arg>::type>::type, uint8_t>::value>::type* =0)
        {
            s << std::forward<arg>(a) << ",";
        }
    }

    namespace cpp14
    {
        template<class arg>
        void print(std::ostream& s, arg&& a, std::enable_if_t<std::is_same<typename std::remove_reference<typename std::remove_cv<arg>::type>::type, uint8_t>::value>* =0)
        {
            s << static_cast<int>(a) << ",";
        }

        template<class arg>
        void print(std::ostream& s, arg&& a, std::enable_if_t<!std::is_same<typename std::remove_reference<typename std::remove_cv<arg>::type>::type, uint8_t>::value>* =0)
        {
            s << std::forward<arg>(a) << ",";
        }
    }

    namespace cpp17
    {
        template<class arg>
        void print(std::ostream& s, arg&& a)
        {
            if constexpr (std::is_same_v<std::remove_reference_t<std::remove_cv_t<arg>>, uint8_t>)
                s << static_cast<int>(a) << ",";
            else
                s << std::forward<arg>(a) << ",";
        }
    }

    namespace cpp20
    {
        template<class arg>
        void print(std::ostream& s, arg&& a)
        {
            if constexpr (std::is_same_v<std::remove_cvref_t<arg>, uint8_t>)
                s << static_cast<int>(a) << ",";
            else
                s << std::forward<arg>(a) << ",";
        }
    }

    template<class Head>
    void print_args(std::ostream& s, Head&& head)
    {
        //cpp11::print(s, std::forward<Head>(head));
        //cpp14::print(s, std::forward<Head>(head));
        //cpp17::print(s, std::forward<Head>(head));
        cpp20::print(s, std::forward<Head>(head));
    }

    template<class Head, class... Tail>
    void print_args(std::ostream& s, Head&& head, Tail&&... tail)
    {
        print_args(s, std::forward<Head>(head));
        print_args(s, std::forward<Tail>(tail)...);
    }
}

template<class... Args>
void print_args(Args&&... args)
{
    detail::print_args(std::cout, std::forward<Args>(args)...);
}

int main()
{
    uint8_t val = 1;
    print_args(std::string("hello"), val);
    print_args("hello", val);
}

Несколько дополнительных примечаний:

  • это только проверка на uint8_t, но вы можете столкнуться с проблемой и для других типов POD, таких как int8_t, byte и т. Д.
  • обычно эта функция, похожая на печать, должна возвращать параметр std::ostream по ссылке: template<...> std::ostream& print(...) {...; return s;}
  • Вы можете легко реализовать std::remove_cvref{_t} с std::remove_cv{_t} и std::remove_reference{_t}
0 голосов
/ 31 августа 2018

У вас есть пара проблем. Для выпуска времени компиляции:

if (std::is_same<Head, uint8_t>::value)

к этому:

if constexpr (std::is_same<Head, uint8_t>::value)

Это потому, что вы хотите скомпилировать тело if только тогда, когда допустимо приведение к uint. То, что время выполнения if равно false, не означает, что код внутри него не должен быть действительным. Constexpr, если решит это.

Далее, ваше сравнение типов слишком строго. Сравнение ссылки с не-ссылкой вернет false. Итак, вы хотите убрать тип перед тестом is_same. Я также предпочитаю is_same_v здесь:

if constexpr (std::is_same_v<std::decay_t<Head>, uint8_t>)

Наконец, ваш базовый случай, последний элемент, все еще нуждается в том же типе «if constexpr», чтобы правильно его печатать, но вы применили эту логику только в основном цикле.

Собираем это вместе:

template<class Head>
void print_args(std::ostream& s, Head&& head) {
    if constexpr (std::is_same_v<std::decay_t<Head>, uint8_t>)
        s << static_cast<int>(head);
    else
        s << head << ",";}

template<class Head, class... Tail>
void print_args(std::ostream& s, Head&& head, Tail&&... tail) {
    if constexpr (std::is_same_v<std::decay_t<Head>, uint8_t>)
        s << static_cast<int>(head) << ",";
    else
        s << head << ",";
    print_args(s, std::forward<Tail>(tail)...);
}

template<class... Args>
void print_args(Args&&... args) {
    print_args(std::cout, std::forward<Args>(args)...);
}

Вы можете выделить общее число, если constexpr, в одну вспомогательную функцию, чтобы уменьшить избыточный код и сохранить его СУХИМЫМ. Но вместо того, чтобы идти туда, я предложу кое-что еще.

Просто заставить его работать не всегда лучшая цель. Рекурсивные шаблоны могут использовать больше памяти в компиляторе и замедлять сборки, поэтому решения, которые избегают рекурсии, лучше, если они дают одинаковые результаты.

В качестве такового рассмотрим выражение сгиба (c ++ 17) и лямбду для вывода кода:

Все вышеперечисленное можно заменить на это :

template<class... Args>
void print_args(Args&&... args) {
    ([](auto&& arg)
    {
        if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, uint8_t>)
            std::cout << static_cast<int>(std::forward<Args>(arg));
        else
            std::cout << arg;
        if (sizeof...(Args) > 1)
            std::cout << ",";
    }(args), ...);
}

Разбивая его, он использует выражение сгиба для оператора запятой, используя IILE (Лямбда-выражение с немедленным вызовом) в форме (f (x) op ...), где f (x) - лямбда (учитывая current arg), а «op» является оператором запятой для последовательности вызовов.) Второе «if» предотвращает конечную запятую.

0 голосов
/ 31 августа 2018

То, что if вычисляется во время выполнения, так что компилятор должен скомпилировать для него обе ветви, а static_cast<int>(head) не может скомпилировать для неарифметических типов (например, std::string).

В C ++ 17 это можно исправить с помощью if constexpr(std::is_same<Head, uint8_t>::value).

До C ++ 17 добавьте дополнительную функцию, которую вы можете перегрузить для uint8_t:

template<class T>
auto convert(T&& t) -> decltype(std::forward<T>(t)) {
    return std::forward<T>(t);
}

int convert(uint8_t t) {
    return t;
}

template<class Head>
void print_args(std::ostream& s, Head&& head) {
    s << convert(std::forward<Head>(head));
}

template<class Head, class... Tail>
void print_args(std::ostream& s, Head&& head, Tail&&... tail) {
    s << convert(std::forward<Head>(head)) << ',';
    print_args(s, std::forward<Tail>(tail)...);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...