Hi,
I've been developing a registration-based reflection library for C++, and I ran into an interesting issue with the MSVC compiler along the way. Anyway, I was able to distill the problematic code into this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
// main.cpp
#include <iostream>
#include <string>
using std::string;
struct TypeInfo
{
////////////////////////
/// Constructors ///
public:
/** Static "Constructor" */
static TypeInfo Create(const string& value)
{
return TypeInfo(value);
}
TypeInfo(const TypeInfo& copy) = delete;
TypeInfo(TypeInfo&& move)
: _name(std::move(move._name))
{
// All done
}
private:
/** Real constructor (managed by 'Create' method) */
TypeInfo(const string& value)
: _name(value)
{
// All done
}
///////////////////
/// Methods ///
public:
/** Named parameter idiom */
TypeInfo& SetSomething()
{
return *this;
}
/** Get the name of this type */
const string& GetName() const
{
return _name;
}
/////////////////////
/// Operators ///
public:
TypeInfo& operator=(const TypeInfo& copy) = delete;
TypeInfo& operator=(TypeInfo&& move) = delete;
////////////////
/// Data ///
private:
string _name;
};
struct SomeClass
{
// SomeClass has reflection information
static const TypeInfo& StaticTypeInfo;
};
const TypeInfo& SomeClass::StaticTypeInfo = TypeInfo::Create("SomeClass");
int main()
{
std::cout << SomeClass::StaticTypeInfo.GetName() << std::endl;
std::cin.get();
}
|
EDIT: It turns out it works just fine, I was just being dumb. But that still doesn't fix the named parameter issue (see below).
It works until the 'named-parameter idiom' stuff is introduced (calling 'SetSomething()' after 'Create' on line 67). I think I know why ('SetSomething()' returns a reference so the compiler doesn't see it as a temporary anymore), but its still stupid as hell. I could of course fix this by opening up the copy constructor, but I'd really rather not because then anyone could inadvertently copy a 'TypeInfo' object which would be
very bad, due to the way they are hooked into the rest of the system in my real codebase.
Are there any other tricks I can pull to get this to work? Also, is Microsoft going to fix their compiler? (You would not believe how much non-standard stuff I've gotten away with in the past using it, however usually they made coding easier, not harder. For example, you can explicitly delete the move constructor for 'TypeInfo', which
should break the 'Create' method, and while it correctly underlines it as an error in intellisense, it still compiles and runs anyway).
EDIT 2:
I figured it out. Its a little ugly, but it works well. And seeing as this is a rather special part of the codebase anyway, ugly is alright. Anyway, first thing you have to do is change 'SomeClass::StaticTypeInfo' from a reference to a value. Then change 'SetSomething()' (along with every name-parameter method) to as follows:
1 2 3 4 5
|
TypeInfo&& SetSomething()
{
// Set stuff
return std::move(*this);
}
|
That way even you're done constructing your type it invokes the move constructor. And its still impossible to accidentally copy a TypeInfo object.