A function from one library mapped to a template from another library

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 ++.

+6
source share
1 answer

This is the final error in spdlog.

The problem is described in detail in this FAQ:
What is an "argument-dependent search" (also known as ADL or "Koenig Lookup")?

Since messaging::TestSerializableObject inherited from the type in the mutils namespace, when convert is called unqualified from the fmt::internal namespace with TestSerializableObject , both fmt::internal::convert and mutils::convert considered in the overload set. Functions Variadic always takes the last place when resolving overloads, so the template argument F in the latter case is better compared to ... in the first and mutils::convert .

This does not apply to your code or mutils in any way - any type with a unary function template or function named convert in the same namespace or parent namespace is susceptible to this problem.

The fix is ​​to qualify the convert call and change the definition of fmt::internal::ConvertToInt<T>::enable_conversion from

 enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) }; 

to

 enum { enable_conversion = sizeof(internal::convert(get<T>())) == sizeof(Yes) }; 

In my own code, I’m used to always filtering all function calls inside any internal / detail namespace, even from code inside the same namespace, unless the use of ADL is explicitly provided. (Nb calls do not have to be fully qualified, just qualified.) I learned that this lesson from watching Boost should deal with this problem with great difficulty, since C ++ 11 appeared: -]

+9
source

Source: https://habr.com/ru/post/1012468/


All Articles