I am working on a C ++ project that uses two different libraries: spdlog for logging and mutils-serialization for serializing objects in bytes (for sending over the network). Both libraries use namespaces correctly, but when I try to write a program that uses both of them at the same time, my compiler (g ++ 6.2) gives me meaningless errors, which seem to indicate that it is trying to create an instance function template from spdlog library using function template definition from mutils library.
Here is my simple test program:
#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
is a simple class that implements mutils::ByteRepresentable
(an interface that allows you to serialize and retrieve the mutils serialization library) and provides operator<<
(which is required for spdlog to register it), I can send code for it if it's necessary.
When I compile this with g++ -std=c++14 -I"./src" -I"./libraries" -I"./libraries/mutils/" -L"./libraries/" -O0 -g3 -Wall "src/LibraryCollisionTest.cpp"
, I get this long, ugly error (donβt worry, I will help you parse it):
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) };
The key line is here:
./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]'
Somehow g ++ jumped from expanding a template function inside the spdlog library in the fmt::internal
namespace to a function template in the mutils library in the mutils namespace, which clearly does not correspond to the spdlog library! If I look at line 1276 from format.h
, this is the one that calls the "convert" function inside this template structure:
template<typename T> struct ConvertToInt { enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) }; enum { value = ConvertToIntImpl2<T, enable_conversion>::value }; };
The few lines above, of course, are the "convert" function:
template <typename T> T &get(); Yes &convert(fmt::ULongLong); No &convert(...);
They are all inside the fmt::internal
namespace, and my IDE agrees that if I want to define the "convert" function on line 1276, I have to go to the "convert" function on line 1248. So why does g ++ ignore this definition and instead try using the definition for mutils::convert()
, which is not even in the right namespace?
Note that clang also fails to compile this program and makes the same error, so I don't think it is a bug in g ++.