2016-11-18 28 views
6

私はロギングにspdlog、オブジェクトをバイトにシリアル化するのにmutils-serializationという2つの異なるライブラリを使用するC++プロジェクトで作業しています。両方のライブラリは名前空間を適切に使用しますが、同時に両方を使用するプログラムを作成しようとすると、私のコンパイラ(g ++ 6.2)では、spdlogライブラリから関数テンプレートをインスタンス化しようとしているような無意味なエラーが表示されますmutilsライブラリの関数テンプレートの定義を使用します。1つのライブラリの関数と別のライブラリのテンプレートが一致しました

は、ここに私の簡単なテストプログラムです:

#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); 
} 

TestSerializableObjectmutils::ByteRepresentable(直列化を可能にし、mutilsシリアライゼーションライブラリで引っ張るインタフェース)を実装する単純なクラスで、要求される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で、namespace mutilsのmutilsライブラリの関数テンプレートに展開しました。これは明らかにspdlogライブラリが意図したものではありません。私はformat.hのライン1276を見れば、それはこのテンプレート構造体の内側に「変換」機能を呼び出す一つだ:

template<typename T> 
struct ConvertToInt 
{ 
    enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) }; 
    enum { value = ConvertToIntImpl2<T, enable_conversion>::value }; 
}; 

上記の数行、案の定、「変換」機能である:

template <typename T> 
T &get(); 

Yes &convert(fmt::ULongLong); 
No &convert(...); 

これらはすべて名前空間fmt::internalの中にあります。私のIDEは、関数の定義を行1276で "変換"したい場合は、行1248の "変換"関数にジャンプする必要があります。代わりにmutils::convert()の定義を使用しようとします。これは適切な名前空間にもありません。

clangもこのプログラムをコンパイルできず、同じ間違いがあることに注意してください。これはg ++のバグではないと思います。

+2

修正はspdlog(https://github.com/gabime/spdlog/commit/d6b34d7b5c62a0000d96939ad946b85a2274f2bb) – GabiMe

+0

で修正されました。ありがとう@GabiMe!私は新しいバージョンをチェックアウトし、私の問題を解決しました。 – Edward

答えて

9

これは間違いなくspdlogのバグです。

問題このFAQに要約し説明されている:
What is “Argument-Dependent Lookup” (aka ADL, or “Koenig Lookup”)?

messaging::TestSerializableObjectため継承を型からTestSerializableObjectと名前空間fmt::internal内側から修飾されていないと呼ばれるconvert名前空間mutilsに、両方fmt::internal::convertmutils::convertがオーバーロードセットと見なされます。 Variadic関数は、オーバーロード解決時に常に最後にランク付けされるため、後者のテンプレート引数Fは、前者の...よりもよく一致し、mutils::convertが選択されます。

これはあなたのコードやmutilsには全く関係がありません–同じ名前空間にconvertという名前の単項関数または関数テンプレートを持つ型があるか、親名前空間がこの問題の影響を受けます。

修正は、私は常にすべてのコールを修飾する習慣を作るconvertコールを修飾し、

私自身のコードで
enum { enable_conversion = sizeof(internal::convert(get<T>())) == sizeof(Yes) }; 

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

からfmt::internal::ConvertToInt<T>::enable_conversionの定義を変更することです任意のinternal/detail名前空間、同じ名前空間内のコードからの内の関数、unより少ないADL使用が明示的に意図されている。 (N.b.コールはである必要はなく、完全にの資格を持つ資格があります。)Boostがこの問題をC++ 11が芽生えているように扱うのを見てこのレッスンを学びました。 : - ]

+2

または、ヘルパーを静的メンバー関数として特性クラスに移動します。名前がクラスメンバーに解決される場合、ADLはありません。 'convert'をカッコで囲むこともできますが、それはあまりにも微妙です。 –

+0

@ T.C。 C++の開発者の90%が知らないことの1つです。本当に微妙です。 – ildjarn

+0

最後の手段として、別々のライブラリからインクルードする場合は次のようにします。自分のcppファイルのそれぞれに2つのライブラリのうちの1つだけを使用する単純な解決方法があります。 –

関連する問題