Prev: How can I alter how a user-defined type/enum is displayed? (facet/locale?)
Next: Confusing behavior of copy constructor
From: Chris Uzdavinis on 1 Jul 2010 07:49 {mod note: To avoid the funky formatting, set your line limit to wrap at about 70 characters.} On Jul 1, 10:02 am, Lorri <steve.lori...(a)gmail.com> wrote: > Hi folks > > For a given user-defined type or enum: > > eg: > enum blah > { > FOO, > BAR}; > > extern std::ostream& operator<<(std::ostream &out, const blah in); > extern const char* const prt(const blah in, const bool full = false); > > How can I alter how the operator<< displays my blah enum? (in some > cases I want the full english name, whereas in other cases I want just > a single char) C++ allows you to overload on types, and so that's a good place to start. Your enum's type doesn't change, but the way you may wish to display it does. Therefore I have found useful practice for various objects to write a thin wrapper class that models the display concept, holding a reference (or copy in the case of enums) of your underlying data. Then overload the stream operators for the wrapper class, rendering as you wish. You might do something like: namespace streaming { template <typename T> struct NameOf { NameOf(T const & obj) : obj_(obj) { } T const & obj_; }; template <typename T> NameOf<T> showSameOf(T obj) { return NameOf<T>(obj); } template <typename T> struct ShortNameOf { NameOf(T const & obj) : obj_(obj) { } T const & obj_; }; template <typename T> ShortNameOf<T> showShortNameOf(T obj) { return ShortNameOf<T>(obj); } } // streaming as a generic concept. Then overload a specialization of operator<< for your enum. Note, for each way of formatting, you have a simple class holder and a helper function to instantiate class template with minimal effort. The above classes could be further reduced to a single macro, so when you have new ideas for formatting, you just declare another line of the macro: #define MAKE_FORMATTER(NAME) \ template <typename T> struct NAME \ { \ NAME(T const & obj) : obj_(obj) { } \ T const & obj_; \ }; \ template <typename T> NAME<T> show##NAME(T obj) \ { \ return NAME<T>(obj); \ } \ MAKE_FORMATTER(NameOf); MAKE_FORMATTER(ShortNameOf); an so on. The above would be generic library code. Below would be your enum-specific code. Note, you can use the MAKE_FORMATTER macro in your code if you have a formatting concept for your class that's not generic (but perhaps could use a similar-but-different macro to generate a non- template version.) // The code defining your enum + associated functions #include "streaming/nameof.hpp" // includes the above code #include <iostream> enum blah { FOO, BAR }; char const * toString(blah b) { switch (blahwrapper) { case FOO: return "FOO"; case BAR: return "BAR"' } return "?"; } std::ostream & operator<<(std::ostream & os, NameOf<blah> > blahwrapper) { return os << toString(blahwrapper.obj_); } std::ostream & operator<<(std::ostream & os, ShortNameOf<blah> > blahwrapper) { return os << toString(blahWrapper.obj_)[0]; } And so on. Now with that all done, users can use your code easily enough: You could even go a step farther and templatize operator<< on NameOf<T> such that it always calls toString on whatever T is, and then you only woudl have to provide toString and NameOf just works with it. Finally, user code might look like this: blah b = FOO; std::cout << "b as a number: " << b << ", spelled: " << streaming::nameOf(b) << ", abbreviated: " << streaming::shortNameOf(b) << endl; Putting it all together: namespace streaming { #define MAKE_FORMATTER(NAME) \ template <typename T> struct NAME \ { \ NAME(T const & obj) : obj_(obj) { } \ T const & obj_; \ }; \ template <typename T> NAME<T> show##NAME(T obj) \ { \ return NAME<T>(obj); \ } \ MAKE_FORMATTER(NameOf) MAKE_FORMATTER(ShortNameOf) } // streaming #include <iostream> enum blah { FOO, BAR }; char const * toString(blah b) { switch (b) { case FOO: return "FOO"; case BAR: return "BAR"; } return "?"; } std::ostream & operator<<(std::ostream & os, streaming::NameOf<blah> wrap) { return os << toString(wrap.obj_); } std::ostream & operator<<(std::ostream & os, streaming::ShortNameOf<blah> wrap) { return os << toString(wrap.obj_)[0]; } // user code int main() { blah b = FOO; std::cout << "b as a number: " << b << ", spelled: " << streaming::showNameOf(b) << ", abbreviated: " << streaming::showShortNameOf(b) << std::endl; } Chris -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |