Closed Kicer86 closed 6 months ago
This issue exists because the current fake_object
implementation is unusable with a non-external linkage class.
The solution is relatively simple. This implementation was the first version, and I found this bug myself too.
@schaumb , you are right, of course. Thanks for the input on how to fix it. By the way, I love your work on Boost.PFR.
duplicated #15
@Kicer86 , I have tried this with GCC 11.4 and remarkably I cannot even reproduce the problem. It compiles and runs through just fine.
However, I can reproduce the issue with Clang-16.
I can confirm that the fix proposed by @schaumb resolves the issue.
However, I am a bit concerned that we are exploiting a bug here. Classes and structs inside unnamed namespaces are internally linked - you should not be able to externally link them.
Maybe the solution is to just come up with a better error message at compile time.
If have now added a static assertion, refer to c71e4ca11c1c953b249de44caf2f4eb3952007cb.
If you are trying to do what @Kicer86 did, you will now get a very clear compilation error that tells you precisely what went wrong. You may try to suppress it by passing REFLECT_CPP_IGNORE_LOCAL_LINKAGE to the compiler and then it might work, because I am using @schaumb's approach.
However, the default is for this not to work, because I am unconvinced that the solution @schaumb proposes does not exploit what is actually a bug in the compilers.
However, I am open to arguments to the contrary.
@schaumb , @Kicer86 , what are your thoughts on this?
I think this is not a bug because later it can be defined in this compilation unit. And because no one uses it, it shouldn't be a linker error. But I could not argue with my statement.
If you are afraid that template extern wrapper<T>
is not what you want to use because of extern
an internal linkage class, replace it with template struct static member. This technique is much older, and no one found this to be problematic. (the definition is inside the class, and the declaration is nowhere)
Note: If you want to use static assertion on types, you must check unnamed classes too.
@schaumb , yes, my major concern was the fact that this workaround does indeed externally link something that should be internally linked. I think this is something that compilers shouldn’t allow. It defies the point of internal linkage. But I will try the other approach that works without the extern keyword. I think this looks very promising.
@schaumb , the latter solution does not work on newer version of MSVC. The compiler (quite accurately) returns the following error:
error C7631: variable with internal linkage declared, but not defined.
I will see what I can do, but if you have any ideas, that would be very appreciated.
EDIT: Never mind, I know what the problem is. I will solve it tomorrow. Thank you so much for your input so far.
you can force MSVC to "not use" the symbol if it is used only in template arguments. https://godbolt.org/z/bWfTTvqo3
@schaumb Yes, that is what I figured out as well, hence the edit. Sorry to have bothered you there. But thank you anyway for the Godbolt example.
I will implement a solution based on that approach tomorrow, but it’s the middle of the night here…
@Kicer86 , I have tried this with GCC 11.4
I have gcc 13.2.1
All right, this issue is finally resolved.
@Kicer86 , thank you for raising this issue.
@schaumb , thank you for all of your input.
By the way, do you want to see something really fucked up? Check out line 77:
https://www.godbolt.org/z/4ThqdnMPz
It's an MSVC-only problem. Luckily, there is a simple fix...
Yes, I know that problem.
This can be used for some hacks, like:
The std::is_constant_evaluated()
function is only available from C++20. But in MSVC because of the member pointer inconsistency (different handling on compile time template, compile time constexpr function, and runtime), I found a C++11 version implementation.
When MSVC checks that the member pointer is already template instantiated, it only compares the offset (like the runtime version).
Another note for the solution:
Probably it would have been easier if the template extern
variable was put in an anonymous namespace. (last block:)
I like your idea of having extern inside of an unnamed namespace. It certainly seems like less of a workaround than the current solution. I will give that a try as well.
When
rfl
is being used on astruct
in an anonymous namespace, gcc complains at linkage stage with errors like:and clang throws erros at compilation time: