Wednesday, April 13, 2011

Encapsulate a simple type using C++ templates

I'm looking to add functionality to all the simple types in C++.

I want to write a single templated class that takes as a template parameter the type to be encapsulated and then has all the operators defined so that the encapsulated class works exactly as the simple type it encapsulates.

Something like this:

template <typename _SimpleType_>
class Attribute
{
public:
    Attribute(_SimpleType_ value){ m_value = value; }
    ~Attribute(){}

    // Cast
    operator _SimpleType_() { return(m_value); }

    // Comparisons
    bool operator==(const a& other) const { return a == m_value; }
    etc...

private:
   _SimpleType_ m_value;
}

// Use like:
Attribute<int> i = 20;

while(i)
{
   if((i & 0xF) == 0)
   {
      i >>= 2;
   }

   i--;
}  etc...

The question is I'm sure there are a load of nuances that have to be dealt with and specialised template operators written; so is there anywhere that this has already been done so that I can just use that instead?

Boost is too large and complicated to put in my project but I can look at it for pointers if there is a class like this in there - whats its name if there is?

From stackoverflow
  • It's pretty simple, if tedious, - you just have to implement all the operators supported by the standard types and where the cast operator is not sufficient.

    I have to ask though, why on earth are you trying to do this?

    mch : Second that, why on earth!?
    Daniel Earwicker : One good reason would be to make the default constructor properly construct the contained type. So no more uninitialized variables.
    Andrew Grant : That is not a good reason IMO!
    Tanveer Badar : Not good enough. Compilers warn about uninitialized variables anyway.
    A. Levy : Maybe YOUR compiler warns about uninitialized variables. Not every compiler is necessarily setup to do so.
  • I'm not sure if boost::ref is what you're looking for.

    At any rate, the best thing you'd do is to just write it out by hand -- but this will start becoming a problem if you intend to support pointer and reference semantics.

    What you'd also proabably need to do is put it in a namespace and implement the free function operator overloads and rely on ADL for it to get picked up. This will get a little unwieldy though as you implement more and more operators.

  • You can get the implementation of the nonmutating operators for free, just by the conversion to _Simple_type_ (and you would get the assignments and increment/decrement by conversion to _Simple_type_&). Another question is whether this is really a good idea, as it creates conversions both T to Attribute<T> and Attribute<T> to T which causes problems while overloading - but you could fix that by making the constructor of Attribute<T> explicit.

    This leaves the assignments and increment/decrement - you would just have to implement those.

    Another possibility is using boost::operators - a header only library that facilitates creation of operator overloads based on algebraic rules. eg. you create operator+=, and it will provide you operator+. You create operator< and operator== and it will give you the other relationals etc.

  • Not to do with your question, but you should be aware that names such as _SimpleType_ (that is, names that begin with an underscore and an uppercase character) are reserved for the C++ compiler and Standard Libarary implementors to use - you are not allowed to use them in your own code.

  • Here is an example of doing with an automatic typecast to T& (tested with GNU C++ 4.3.2):

    #include <iostream>
    
    using namespace std;
    
    template <typename T>
    class Attribute {
    public:
        Attribute(const T &value) { v = value; }
        operator T & () { return v; }
    private:
        T v;
    };
    
    int main(int argc, char **argv)
    {
        Attribute<int> i(0);
        i = 3;
        i++;
        i += 4;
        i = i + 5;
        i <<= 3;
        cout << "i is now " << i << endl;
    }
    

    The C++ compiler casts automagically the reference to 'Attribute' to a reference to 'int' using the coercion operator 'operator T & ()'. So when the Attribute class does not provide the '++' operator or anything, the object is typecasted to int & and then the operator is looked up from there. Feel free to experiment.

  • I like this form of encapsulation of simple types (original author - Sektor van Skijlen):

    template<typename T>
    class explicit_t
    {
    private:
        T value;
    
        template<typename V> explicit_t(V t);
    public:
        operator T&() {return value;}
        explicit_t(const T& c) : value(c) {}    
    };
    

    And the short example:

    void fun(explicit_t<int> foo) {}
    
    int main()
    {
            // fun('a');
            // fun(3u);
            // fun(3.0);
            fun(4);
    }
    

    So what do I get? No more unwanted conversions.

    You might also want to have a look at something more fancy - typegen.

0 comments:

Post a Comment

Note: Only a member of this blog may post a comment.