morganstanley/binlog

BINLOG_ADAPT_ENUM fails for nested enums

andrewkcorcoran opened this issue · 11 comments

namespace ns {
   struct Test
   {
      enum class Enum
      {
         value
      } Enum;
   };
} // namespace ns
BINLOG_ADAPT_ENUM(ns::Test::Enum)

results in template argument 1 is invalid at include/binlog/adapt_enum.hpp:4 changing it to BINLOG_ADAPT_ENUM(enum ns::Test::Enum) fixes the error in clang but results in declaration enum class ns::Test::Enum does not declare anything error on gcc at include/mserialize/make_struct_serializable.hpp:7

Hi, the issue is that Enum is both the type name and the field name, making the ns::Test::Enum expression reference the field, not the type. Is it possible to make these two different?

Let's see if CI likes this: #125

It did. Please reopen if it doesn't fix your issue

Just realised this change may break old c style enums :/

typedef enum {
   field1
} name;

Unfortunate. In C++20, using enum works. I don't see a way to solve this with a single macro.

I don't have a solution that works for both enums shadowed by values (Enum in the OP) and typedefs of enums, on clang, msvc and gcc. The closest I got is this:
https://github.com/morganstanley/binlog/pull/126/checks?check_run_id=2926324358
that works for every case even on GCC9, but fails on clang and msvc, and I suspect it is ill-formed code (see https://stackoverflow.com/questions/40523513/typename-type-members-and-non-type-members-is-it-valid-code)

I can recommend this workaround:

namespace ns {
   struct Test
   {
      enum class Enum
      {
         value
      } Enum;
      using EnumT = enum Enum;  // workaround
   };
} // namespace ns
BINLOG_ADAPT_ENUM(ns::Test::EnumT)

what do you think?

That doesn't seem too onerous, but won't work for adapting code that's not owned (unmodifiable) by the user.

What about this?

// vendor code, unmodified
namespace ns {
   struct Test
   {
      enum class Enum
      {
         value
      } Enum;
      using EnumT = enum Enum;  // workaround
   };
} // namespace ns

// user code
namespace ns {
      using TestEnum = enum Test::Enum;  // workaround
   };
} // namespace ns
BINLOG_ADAPT_ENUM(ns::TestEnum)

(Or do not even re-open the vendors namespace, just use NsTestEnum)

Ideal would be to make BINLOG_ADAPT_ENUM(enum ns::Test::Enum) work, but I couldn't figure out how, without adding yet another macro, and keeping typedefs supported.

Yea that should solve the issue of thirdparty code. It's not ideal but as long as we add a limitations section to the documentation with details of how to workaround it I think that acceptable.

Done in #128