Я работаю над проектом C ++, который использует две разные библиотеки: spdlog для регистрации и mutils-сериализации для сериализации объектов в байты (для отправки по сети). Обе библиотеки правильно используют пространства имен, но когда я пытаюсь написать программу, которая использует оба из них одновременно, мой компилятор (g ++ 6.2) выдает мне бессмысленные ошибки, которые, кажется, указывают на то, что он пытается создать экземпляр шаблона функции из библиотеки spdlog используя определение шаблона функции из библиотеки mutils.
Вот моя простая тестовая программа:
#include <spdlog/spdlog.h>
#include <spdlog/fmt/ostr.h>
#include "TestSerializableObject.h"
int main(int argc, char** argv) {
auto global_logger = spdlog::rotating_logger_mt("global_logger", "log", 1024 * 1024 * 500, 3);
global_logger->set_pattern("[%H:%M:%S.%e] [%l] %v");
global_logger->set_level(spdlog::level::trace);
std::shared_ptr<spdlog::logger> logger(spdlog::get("global_logger"));
auto message = std::make_shared<messaging::TestSerializableObject>(
1, 2, "A message!");
logger->trace("Received a message: {}", *message);
}
TestSerializableObject
это простой класс, который реализует mutils::ByteRepresentable
(интерфейс, который позволяет сериализацию и извлекает из библиотеки mutils-serialization), и обеспечивает operator<<
(что требуется для того, чтобы spdlog мог его регистрировать). Я могу опубликовать код для этого при необходимости.
Когда я собираю это с g++ -std=c++14 -I"./src" -I"./libraries" -I"./libraries/mutils/" -L"./libraries/" -O0 -g3 -Wall "src/LibraryCollisionTest.cpp"
Я получаю эту длинную, уродливую ошибку (не волнуйтесь, я помогу вам разобрать ее):
In file included from ./libraries/mutils/mutils.hpp:3:0,
from ./libraries/mutils-serialization/SerializationSupport.hpp:2,
from src/TestSerializableObject.h:10,
from src/LibraryCollisionTest.cpp:10:
./libraries/mutils/args-finder.hpp: In instantiation of ‘struct mutils::function_traits<messaging::TestSerializableObject>’:
./libraries/mutils/args-finder.hpp:75:41: required from ‘auto mutils::convert(F) [with F = messaging::TestSerializableObject; ignore = void]’
./libraries/spdlog/fmt/bundled/format.h:1276:46: required from ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12: required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5: required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9: required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5: required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53: required from here
./libraries/mutils/args-finder.hpp:12:37: error: ‘operator()’ is not a member of ‘messaging::TestSerializableObject’
: public function_traits<decltype(&T::operator())>
^~
./libraries/mutils/args-finder.hpp: In instantiation of ‘auto mutils::convert(F) [with F = messaging::TestSerializableObject; ignore = void]’:
./libraries/spdlog/fmt/bundled/format.h:1276:46: required from ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12: required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5: required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9: required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5: required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53: required from here
./libraries/mutils/args-finder.hpp:75:41: error: ‘as_function’ is not a member of ‘mutils::function_traits<messaging::TestSerializableObject>’
return function_traits<F>::as_function(f);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
In file included from ./libraries/spdlog/fmt/fmt.h:21:0,
from ./libraries/spdlog/common.h:41,
from ./libraries/spdlog/spdlog.h:12,
from src/LibraryCollisionTest.cpp:8:
./libraries/spdlog/fmt/bundled/format.h: In instantiation of ‘struct fmt::internal::ConvertToInt<messaging::TestSerializableObject>’:
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of ‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const T&, typename fmt::internal::EnableIf<fmt::internal::Not<fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T = messaging::TestSerializableObject]’
./libraries/spdlog/fmt/bundled/format.h:2465:12: required from ‘static fmt::internal::Value fmt::internal::ArgArray<N, true>::make(const T&) [with Formatter = fmt::BasicFormatter<char>; T = messaging::TestSerializableObject; unsigned int N = 1u]’
./libraries/spdlog/fmt/bundled/format.h:2898:5: required from ‘void fmt::BasicWriter<Char>::write(fmt::BasicCStringRef<CharType>, const Args& ...) [with Args = {messaging::TestSerializableObject}; Char = char]’
./libraries/spdlog/details/logger_impl.h:69:9: required from ‘void spdlog::logger::log(spdlog::level::level_enum, const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
./libraries/spdlog/details/logger_impl.h:127:5: required from ‘void spdlog::logger::trace(const char*, const Args& ...) [with Args = {messaging::TestSerializableObject}]’
src/LibraryCollisionTest.cpp:21:53: required from here
./libraries/spdlog/fmt/bundled/format.h:1276:38: warning: invalid application of ‘sizeof’ to a void type [-Wpointer-arith]
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
Ключевая строка здесь:
./libraries/mutils/args-finder.hpp: In instantiation of ‘auto mutils::convert(F)
[with F = messaging::TestSerializableObject; ignore = void]’:
./libraries/spdlog/fmt/bundled/format.h:1276:46: required from ‘struct
fmt::internal::ConvertToInt<messaging::TestSerializableObject>’
./libraries/spdlog/fmt/bundled/format.h:1485:5: required by substitution of
‘template<class T> fmt::internal::MakeValue<Formatter>::MakeValue(const
T&, typename fmt::internal::EnableIf<fmt::internal::Not<
fmt::internal::ConvertToInt<T>::value>::value, int>::type) [with T =
messaging::TestSerializableObject]’
Каким-то образом g ++ не стал расширять шаблонную функцию внутри библиотеки spdlog в пространстве имен fmt::internal
, к шаблону функции в библиотеке mutils, в пространстве имен mutils
Это явно не то, что собиралась делать библиотека spdlog! Если я посмотрю на строку 1276 format.h
это тот, который вызывает функцию «convert» внутри этой структуры шаблона:
template<typename T>
struct ConvertToInt
{
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
};
Несомненно, несколько строк выше — это функция «convert»:
template <typename T>
T &get();
Yes &convert(fmt::ULongLong);
No &convert(...);
Это все внутри пространства имен fmt::internal
и моя среда IDE соглашается с тем, что если мне нужно определение функции «convert» в строке 1276, я должен перейти к функции «convert» в строке 1248. Так почему g ++ игнорирует это определение и вместо этого пытается использовать определение для mutils::convert()
что даже не в правильном пространстве имен?
Обратите внимание, что clang также не может скомпилировать эту программу и допускает ту же ошибку, поэтому я не думаю, что это ошибка в g ++.
Это определенно ошибка в spdlog.
проблема кратко описывается в этом FAQ:
Что такое «Аргумент-зависимый поиск» (он же ADL или «Поиск Кенига»)?
Так как messaging::TestSerializableObject
наследуется от типа в пространстве имен mutils
, когда convert
называется неквалифицированным изнутри пространства имен fmt::internal
с TestSerializableObject
, и то и другое fmt::internal::convert
а также mutils::convert
рассматриваются в наборе перегрузки. Функции Variadic всегда ранжируются последними во время разрешения перегрузки, поэтому аргумент шаблона F
в последнем лучше подходит, чем ...
в первом и mutils::convert
выбран.
Это никоим образом не относится к вашему коду или к mutils — любой тип с унарной функцией или шаблоном функции с именем convert
в том же пространстве имен или родительском пространстве имен подвержены этой проблеме.
фиксировать это квалифицировать convert
позвонить и изменить определение fmt::internal::ConvertToInt<T>::enable_conversion
от
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
в
enum { enable_conversion = sizeof(internal::convert(get<T>())) == sizeof(Yes) };
В моем собственном коде я привык всегда квалифицировать все вызовы функций внутри любого internal
/detail
Пространство имен, даже из кода в том же пространстве имен, если использование ADL явно не предусмотрено. (Н.б. звонки не должны быть от корки до корки квалифицированный, просто квалифицированный.) Я усвоил этот урок, наблюдая, как Boost должен был решить эту проблему трудным путем, когда появлялся C ++ 11. : -]
Других решений пока нет …