C++ Streamable Variants

so, I wrote a tidbit a while back about a "variable" c++ type based on boost::any (and a few other implementations). It used the p-impl paradigm (pointer to implementation) like so:

class variable_impl_base;
template <typename T>
class variable_impl : public variable_impl_base { T* value; }

class variable { variable_impl_base* p; }

effectively holding a real implementation of whatever type the variable is wrapping...

variable v = 32; //v.p points to a variable_impl<int>

well, that works fine, however, there is no easy way to add a conversion interface around it... that is, "string s = v;" can't be used easilly, without knowing the original type (which may be a good thing, but that's not what I was trying to accomplish). This implementation would require string s = v.to_string(); or something similar...

enter c++ streams!

int i = 32;
std::stringstream ss;
ss < < 32;

string s = ss.str();

tada! the other way is just as easy:

string s("32");
std::stringstream ss(s); //ctor for simplicity/efficiency
int i = 0;
ss >> i;

I'll leave the generic implementation (templated) as an exercise for the reader (always wanted to say that).

From an external standpoint, it appears this could easilly be shoe-horned into the variable type... though we need some helpers:

template <typename T>
T from_string(const std::string& str, const T& def=T())
{
    std::istringstream is(str);
    if(is)
    {
        T val;
        if(is >> val && is.str().length() == 0)
        return val;
    }
    return def;
}

template <typename T>
std::string to_string(const T& t)
{
    std::ostringstream os;
    os << std::fixed << t;
    return os.str();
}

now, simply adding a virtual "to_string" method to variable_impl_base should allow for the variable class to call from_string(p->to_string()) properly. The only stipulation is that the type wrapper is properly streamable (to be done with a modified version of boost::concept_check) -

template <typename T>
struct streamable_concept
{
    T t;
    void check() { std::stringstream ss; ss < < t; ss >> t; }
};

#define require_streamable(type) \
    typedef void (streamable_concept<type>::*is_##type##_streamable)();\
    template <is_##type##_streamable T> struct check_##type##_streamable {};\
    typedef check_##type##_streamable<streamable_concept<type>::check> concept;

Hooray for metaprogramming - for the record, the point of the "define" statement is so that the compiler will find an instance of the templated function which is not actually called (the check function will fail). C++ compilers will not compile templated functions unless they are used somewhere with a specific type - so the define creates an instance which will be optimized out later...

require_streamable(int) equates to: typedef void (streamable_concept::*is_int_streamable)(); template struct check_int_streamable {}; typedef check_int_streamable<streamable_concept::check> concept;

prev next

| | comments