tags:

views:

686

answers:

4

I've stumbled across this great post about validating parameters in C#, and now I wonder how to implement something similar in C++. The main thing I like about this stuff is that is does not cost anything until the first validation fails, as the Begin() function returns null, and the other functions check for this.

Obviously, I can achieve something similar in C++ using Validate* v = 0; IsNotNull(v, ...).IsInRange(v, ...) and have each of them pass on the v pointer, plus return a proxy object for which I duplicate all functions.

Now I wonder whether there is a similar way to achieve this without temporary objects, until the first validation fails. Though I'd guess that allocating something like a std::vector on the stack should be for free (is this actually true? I'd suspect an empty vector does no allocations on the heap, right?)

A: 

Can't say much to the rest of the question, but I did want to point out this:

Though I'd guess that allocating something like a std::vector on the stack should be for free (is this actually true? I'd suspect an empty vector does no allocations on the heap, right?)

No. You still have to allocate any other variables in the vector (such as storage for length) and I believe that it's up to the implementation if they pre-allocate any room for vector elements upon construction. Either way, you are allocating SOMETHING, and while it may not be much allocation is never "free", regardless of taking place on the stack or heap.

That being said, I would imagine that the time taken to do such things will be so minimal that it will only really matter if you are doing it many many times over in quick succession.

Toji
A: 

I recommend to get a look into Boost.Exception, which provides basically the same functionality (adding arbitrary detailed exception-information to a single exception-object).

Of course you'll need to write some utility methods so you can get the interface you want. But beware: Dereferencing a null-pointer in C++ results in undefined behavior, and null-references must not even exist. So you cannot return a null-pointer in a way as your linked example uses null-references in C# extension methods.

For the zero-cost thing: A simple stack-allocation is quite cheap, and a boost::exception object does not do any heap-allocation itself, but only if you attach any error_info<> objects to it. So it is not exactly zero cost, but nearly as cheap as it can get (one vtable-ptr for the exception-object, plus sizeof(intrusive_ptr<>)).

Therefore this should be the last part where one tries to optimize further...

gimpf
A: 

Re the linked article: Apparently, the overhaead of creating objects in C# is so great that function calls are free in comparison.

I'd personally propose a syntax like

Validate().ISNOTNULL(src).ISNOTNULL(dst);

Validate() contructs a temporary object which is basically just a std::list of problems. Empty lists are quite cheap (no nodes, size=0). ~Validate will throw if the list is not empty. If profiling shows even this is too expensive, then you just change the std::list to a hand-rolled list. Remember, a pointer is an object too. You're not saving an object just by sticking to the unfortunate syntax of a raw pointer. Conversely, the overhead of wrapping a raw pointer with a nice syntax is purely a compile-time price.

PS. ISNOTNULL(x) would be a #define for IsNotNull(x,#x) - similar to how assert() prints out the failed condition, without having to repeat it.

MSalters
+1  A: 

Other than the fact that C++ does not have extension methods (which prevents being able to add in new validations as easily) it should be too hard.

class Validation
{
    vector<string> *errors;
    void AddError(const string &error)
    {
       if (errors == NULL) errors = new vector<string>();
       errors->push_back(error);
    }

public:
    Validation() : errors(NULL) {}
    ~Validation() { delete errors; }

    const Validation &operator=(const Validation &rhs)
    {
        if (errors == NULL && rhs.errors == NULL) return *this;
        if (rhs.errors == NULL)
        {
            delete errors;
            errors = NULL;
            return *this;
        }
        vector<string> *temp = new vector<string>(*rhs.errors);
        std::swap(temp, errors);
    }

    void Check()
    { 
         if (errors)
             throw exception();
    }

    template <typename T>
    Validation &IsNotNull(T *value)
    {
        if (value == NULL) AddError("Cannot be null!");
        return *this;
    }

    template <typename T, typename S>
    Validation &IsLessThan(T valueToCheck, S maxValue)
    {
        if (valueToCheck < maxValue) AddError("Value is too big!");
        return *this;
    }

    // etc..

};


class Validate
{
public:
    static Validation Begin() { return Validation(); }
};

Use..

Validate::Begin().IsNotNull(somePointer).IsLessThan(4, 30).Check();
Eclipse
That's pretty much what I have in place now, I was just wondering whether there is some better scheme for delayed creation of objects (kinda construct-on-first-use-pointer) readily available.
Anteru
Well, when you put this on the stack, the cost is effectively incrementing the stack pointer, so you really don't pay much for it - with no vtable and only one pointer in the class, it's as cheap to allocate as a NULL pointer.
Eclipse