специализация шаблона функции для шаблонной функции variadi c (printf) - PullRequest
1 голос
/ 15 марта 2020

У меня есть некоторый код Arduino C ++ 11, который я пытаюсь улучшить: пытаюсь заставить printf-подобную функцию обрабатывать String специально, чтобы мне не приходилось вызывать c_str () самостоятельно везде, где я ее использую. В основном, для любого встроенного типа, такого как int float bool et c, я просто хочу передать arg как есть, а для String pass вернуть c_str (). Ударил несколько препятствий, поэтому я попробовал это в некоторых доступных онлайн-компиляторах. Отправной точкой является следующее: использование std :: string вместо String:

#include <string>

class SerialOut {
public:
    template<class ...Ts>
    static void error(const char* msg, Ts... args) {
        printf(msg, args...);
    }
};

int main() {
    std::string greeting("hi");
    SerialOut::error("Message %d %s\n", 1, greeting.c_str());
}

Поэтому я попытался создать шаблон функции, который просто возвращает полученное значение со специализацией для std :: string:

#include <string>

template <typename T, typename R=T> R raw(T& x) {return x;}
template <> const char* raw<>(std::string& x) {return x.c_str();}

class SerialOut {
public:
    template<class ...Ts>
    static void error(const char* msg, Ts... args) {
        printf(msg, raw(args)...);
    }
};

int main() {
    std::string greeting("hi");
    SerialOut::error("Message %d %s\n", 1, greeting);
}

Я получаю ошибку компиляции при запуске в https://repl.it/languages/cpp11:

clang version 7.0.0-3~ubuntu0.18.04.1 (tags/RELEASE_700/final)
 clang++-7 -pthread -std=c++11 -o main main.cpp
main.cpp:10:25: error: cannot pass object of non-trivial type
      'std::__cxx11::basic_string<char>' through variadic function; call will abort at
      runtime [-Wnon-pod-varargs]
            printf(msg, raw(args)...);
                        ^
main.cpp:16:20: note: in instantiation of function template specialization
      'SerialOut::error<int, std::__cxx11::basic_string<char> >' requested here
        SerialOut::error("Message %d %s\n", 1, greeting);
                   ^
1 error generated.
compiler exit status 1

С https://www.onlinegdb.com/online_c++_compiler ошибки нет, но специализация raw () не выбрана, поэтому вывод для приветствия является мусором.

В Arduino IDE я получаю немного другую ошибку (конечно, после замены std :: string на String):

sketch\mqtt.cpp.o: In function `char const* raw<String, char const*>(String&)':

sketch/utils.h:15: multiple definition of `char const* raw<String, char const*>(String&)'

sketch\Thermistor.cpp.o:sketch/utils.h:15: first defined here

sketch\sketch.ino.cpp.o: In function `char const* raw<String, char const*>(String&)':

sketch/utils.h:15: multiple definition of `char const* raw<String, char const*>(String&)'

sketch\Thermistor.cpp.o:sketch/utils.h:15: first defined here

Я попробовал несколько вариантов функций raw(), чтобы безрезультатно. Я полагаю, что мне просто не хватает тонкости, или просто невозможно сделать это в C ++ 11.

Обновление : я обнаружил Variadi c Макрос: не могу пропустить объекты нетривиально копируемого типа через '...' Один из ответов решает вышеизложенное в C ++ 14 (в основном используется decltype(auto) и перегрузка вместо специализации). Я добавил небольшую вариацию, которая работает также в C ++ 11, и с «inline» она также работает в Arduino C ++ (без «inline» при перегрузке, вышеприведенное сообщение о множественных определениях - оказывается, что это компоновщик сообщение, поэтому он компилируется, я думаю, что вариант Arduino не содержит встроенных «явно встроенных» функций, как другие компиляторы).

Ответы [ 2 ]

0 голосов
/ 15 марта 2020

На основе Variadi c Макрос: не может передавать объекты нетривиально копируемого типа через '...' Я получил его для работы с этим очень простым изменением, которое работает в C ++ 11 и Arduino C ++:

#include <string>

template <typename T> T raw(const T& x) {return x;}
inline const char* raw(const String& x) {return x.c_str();}

class SerialOut {
public:
    template<class ...Ts>
    static void error(const char* msg, Ts... args) {
        printf(msg, raw(args)...);
    }
};

int main() {
    std::string greeting("hi");
    SerialOut::error("Message %d %s\n", 1, greeting);
}

Благодаря комментарию @IgorTandetnik понятно, почему.

0 голосов
/ 15 марта 2020

Что-то в этом роде, возможно:

template <typename T>
struct SerialHelper {
    static T raw(T val) { return val; }
};

template <>
struct SerialHelper<std::string> {
    static const char* raw(const std::string& val) { return val.c_str(); }
};


class SerialOut {
public:
    template<class ...Ts>
    static void error(const char* msg, Ts... args) {
        printf(msg, SerialHelper<Ts>::raw(args)...);
    }
};

Демо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...