I've been working a lot over the past 3 weeks though, so I kind of have an excuse (not really, seeing as how no matter how long I work at my job, I still come home and put in between 4-12 hours programming; depending on the day (I average about 6 hours programming time every night.)
Anyway, the only thing I've accomplished code-wise is...well, really nothing. I stripped my string utility functions out of my code base (because I discovered boost::algorithm::string, which replaced pretty much every one of them.)
While screwing off with little ideas that pop in my head (like I usually do), I can up with an enum-value class and a flag-value class (trying to 'fix' the enum situation in C++ is something I keep coming back to; for some reason I find it fun. Plus, I learn lots of little quirks of the language while doing so.)
Here it is in all of its (horrid, horrid) glory:
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=namespace dbeals{ //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= template struct EnumValue { EnumerationType Value; EnumValue() { } EnumValue(EnumerationType Value) { this->Value = Value; } EnumValue(const string &Name, EnumerationType Value) { this->Value = Value; std::pair::iterator, bool> Result = EnumValue::_container().insert(std::make_pair(Value, Name)); if(!Result.second) throw std::exception((format("The enum value '%1%' has already been registered with the name '%2%'.") % Value % Result.first->second).str().c_str()); } const string &ToString() const { std::map::iterator Itor = _container().find(Value); if(Itor == _container().end()) { try { static string Dummy = ""; Dummy = boost::lexical_cast(this->Value); return Dummy; } catch(boost::bad_lexical_cast) { throw std::exception((format("Failed to cast the value '%1%' to a string.") % this->Value).str().c_str()); } } return Itor->second; } static EnumerationType Parse(const string &Text) { string Copy = String::trim_copy(Text); for(std::map::iterator Itor = _container().begin(); Itor != _container().end(); ++Itor) { if(String::equals(Copy, Itor->second)) return Itor->first; } throw std::exception((format("The enum-value '%1%' does not exist in this enumeration.") % Text).str().c_str()); } operator EnumerationType() const { return this->Value; } const EnumValue &operator =(const EnumValue &Value) { this->Value = Value.Value; return *this; } const EnumValue &operator =(EnumerationType Value) { this->Value = Value; return *this; } private: static std::map &_container() { static std::map Enum; return Enum; } }; //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= template struct FlagValue { EnumerationType Value; FlagValue() { } FlagValue(EnumerationType Value) { this->Value = Value; } FlagValue(const string &Name, EnumerationType Value) { this->Value = Value; std::pair::iterator, bool> Result = FlagValue::_container().insert(std::make_pair(Value, Name)); if(!Result.second) throw std::exception((format("The enum value '%1%' has already been registered with the name '%2%'.") % Value % Result.first->second).str().c_str()); } string ToString() const { string Output = ""; for(std::map::iterator Itor = _container().begin(); Itor != _container().end(); ++Itor) { if(this->Value & Itor->first) { if(!Output.empty()) Output += "|"; Output += Itor->second; } } return Output; } static EnumerationType Parse(const string &Text) { std::vector Tokens; String::split(Tokens, Text, compare_with('|')); EnumerationType Output = (EnumerationType)0; string Copy; for(std::vector::iterator StrItor = Tokens.begin(); StrItor != Tokens.end(); ++StrItor) { for(std::map::iterator Itor = _container().begin(); Itor != _container().end(); ++Itor) { if(String::equals(String::trim(*StrItor), Itor->second)) Output |= Itor->first; } } return Output; } operator EnumerationType() const { return this->Value; } FlagValue operator |(const FlagValue &Value) const { return FlagValue(EnumerationType(this->Value | Value.Value)); } const FlagValue &operator |=(const FlagValue &Value) { this->Value |= Value.Value; return *this; } FlagValue operator |(EnumerationType Value) const { return FlagValue(EnumerationType(this->Value | Value)); } const FlagValue &operator |=(EnumerationType Value) { this->Value |= Value; return *this; } FlagValue operator &(const FlagValue &Value) const { return FlagValue(EnumerationType(this->Value & Value.Value)); } const FlagValue &operator &=(const FlagValue &Value) { this->Value &= Value.Value; return *this; } FlagValue operator &(EnumerationType Value) const { return FlagValue(EnumerationType(this->Value & Value)); } const FlagValue &operator &=(EnumerationType Value) { this->Value &= Value; return *this; } const FlagValue &operator =(const FlagValue &Value) { this->Value = Value.Value; return *this; } const FlagValue &operator =(EnumerationType Value) { this->Value = Value; return *this; } private: static std::map &_container() { static std::map Enum; return Enum; } };}
I made a couple examples using some macros to make it easier to read:
namespace BuildTypes{ enum enum_type { }; db_EnumValue(enum_type, DebugBuild); db_EnumValue(enum_type, ReleaseBuild);} db_DeclEnumType(BuildTypes::enum_type, BuildType);int main(){ BuildType MyBuildType;#if defined(_DEBUG) || defined(DEBUG) MyBuildType = BuildTypes::DebugBuild;#else MyBuildType = BuildTypes::ReleaseBuild;#endif std::cout< return 0;}
And some flags:
namespace BuildOptions{ enum enum_type { }; db_FlagValue(enum_type, IncludeComments); db_FlagValue(enum_type, IncludeWarnings); db_FlagValue(enum_type, IncludeErrors);} db_DeclFlagType(BuildOptions::enum_type, BuildOption);int main(){ BuildOption Options; Options = BuildOptions::IncludeComments | BuildOptions::IncludeErrors; BuildOptions ParseTest1 = BuildOption::Parse("IncludeComments"); BuildOptions ParseTest2 = BuildOption::Parse("includeComments"); // Fails; case-sensitive BuildOptions ParseTest3 = BuildOption::Parse("IncludeComments | IncludeWarnings|IncludeErrors"); // Works fine; spaces are trimmed std::cout<"\n"
std::cout<<(BuildOptions::IncludeWarnings | BuildOptions::IncludeErrors).ToString()< return 0;
}
This has pretty much everything wrong with it that wrapping intrinsic types does, plus probably a bunch of other stuff. One of the other things I note is I'm pretty sure the parse method has a large chance of failing (seeing as how I register the values in their constructor and I'm pretty positive I'm not garaunteed that all of these are constructed when I'm wanting them to be.)
The ToString() and Parse() methods actually aren't too slow though; at each interval of iterations it ran only a fraction of the time longer (less than a quarter of a second.)
I honestly wish it didn't have the problems I'm pretty sure it has, because I'd stick with it then. Either way, I had fun doing it and learned a couple things, so the time wasn't COMPLETELY wasted (ok, it was; shut up [razz].)
I spent the other night trying to come up with a nice content system; I pretty much failed. I had something similar to XNA's content system, but shittier. Plus, it's kind of hard to emulate the OpaqueData parts (I can, and it's pretty type-safe as long as I use RTTI and typeid(), but that slows it down.) I'm thinking about having another go at it, but using boost::any instead of a byte-storage class.
Another lab-project I've been working on is a robust buffer system. The idea behind it was coming up with a buffer class that I could easily use to create/manage memory pools (error buffers, serialization, etc), read the contents of a file (deserialization), stream linear chunks from a file (serializing/deserializing lists), and stream chunks from a file in a grid-based manner (streaming maps.) Pretty much just trying to come up with some generalized solutions for some of the things I've thought about implementing into a game (or know I'll need in a game; like serialization.)
Figured out some of the things I've been curious about (getting the name of the operating system, enumerating disk-drives, calculating available disk-space, calculating available memory, and calculating processor speed.)
Lol, I always look back at what I've done over a period of time and think to myself "If I spent half of the energy on making a game that I put into screwing around ('learning stuff'), I'd have finished SOMETHING by now." Most of my energy seems to go into writing frameworks and wrapping shit.
On the up-side, the design document for The Alchemist is almost finished and after tonight (Sunday night), I have 4 days off. I'm going to try really hard to get the GDD finished and even harder to get the TDD done before I head back to work.
Too much blathering and straying and not enough sleeping; I've been up for about 20 hours and it's time for bed. Gnight!