7

私は 2 つの異なるライブラリを使用する C++ プロジェクトに取り組んでいます。ロギング用のspdlogと、オブジェクトをバイトにシリアル化するためのmutils-serialization (ネットワーク経由で送信するため)。どちらのライブラリも名前空間を適切に使用していますが、両方を同時に使用するプログラムを作成しようとすると、コンパイラ (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);
}

TestSerializableObjectmutils::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 ライブラリ内のテンプレート化された関数の展開から、名前空間内の mutils ライブラリ内のfmt::internal関数テンプレートにジャンプしました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 };
};

上記の数行は、確かに、関数「変換」です。

template <typename T>
T &get();

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

これらはすべて namespace 内fmt::internalにあり、私の IDE は、1276 行目の関数「convert」の定義が必要な場合は、1248 行目の関数「convert」にジャンプする必要があることに同意します。では、なぜ g++ はこの定義を無視し、代わりにmutils::convert()正しい名前空間にさえないの定義を使用してみてください。

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

4

1 に答える 1

10

これは間違いなくバグですspdlogspdlog によって内部的に使用される fmtlib。

この問題は、次の FAQ に要約されてい
ます。

messaging::TestSerializableObjectは namespace の型から継承されるためmutilsconvertが を含む名前空間内から非修飾で呼び出されるfmt::internalと、 と のTestSerializableObject両方がオーバーロード セットと見なされますfmt::internal::convert mutils::convert可変個引数関数は、オーバーロードの解決中に常に最後にランク付けされるため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) };

私自身のコードでは、ADL の使用が明示的に意図されていない限り、同じ名前空間内のコードからであっても、任意のinternal/名前空間内の関数へのすべての呼び出しを常に修飾する習慣をつけています。(Nb 呼び出しは完全にdetail修飾されている必要はなく、単に修飾されているだけです。) C++11 が登場するにつれて、Boost がこの問題に難しい方法で対処しなければならないのを見て、この教訓を学びました。

于 2016-11-18T21:50:28.853 に答える