/** * @file

Restricted & TStaticPredicate

*/
#define C++03
#define INVARIANTS
#define INTERFACE_COMPOSITION

Nomen omen

Name should imply restriction of some original set of values to some smaller subset. It could be for example restriction of set of all "bears" to set of "adult male bears". In more closer to keyboard sense it can be about being able to express "integers greater than 42" by static type if you wish to do so.

Motivation

I got this idea when working with boost::gregorian::date and boost::posix_time::ptime after I discovered that both types can have special values - like infinity or not-a-date/time. These types were used heavily in code base and I didn't want to introduce value checking to billion places of my code only because these types are de facto standard in codebase. That would be unnecessary complicated = bad code. On the other hand I didn't want to ignore the possibility of getting passed instance with special value. Later I realized other similar cases such as interface accepting only non empty std::string or non-empty std::vector.

The simplest solution would be a set of types representing desired entities. But all these use cases are similar in that there are already existing well-working "standard" (at least on preexisting codebase level) representations of their supersets. Ignoring these and writing it from scratch would be nonsense. Had I decided to wrap these I would be forced to duplicate their usually not small interfaces. On the other hand I just wanted to restrict set of possible values by certain compile-time-static rule so the wrapper could be in fact minimal. All I needed it was to represent some guarantee after checking the original value and enabling access to raw data represented by standard types. This is the same no matter what the base type is and no matter what (static) restriction I need to impose on values. Welcome templates!

As an afterthought I realized that there's also one other use case where Restricted might come handy. Semantic representation of return values or member objects. Have function returning std::vector which is always non-empty? The best you can do is again to create custom type and duplicate std::vector interface, less optimal is a comment or documentation.

Design

I am presenting utility designed with two parts - first part is common Restricted template and second part being exemplar StaticPredicate types.

Restricted is a two parameter template. First parameter TBaseType represents superset of possible values. Second parameter is templated TStaticPredicate specifying subset of valid values of base type. Predicates static in relation to runtime were sufficient for my use cases so I was able to use (also static) types for implementation.

You will find implementation comments in tooltips when you hover cursor over such code.

restricted.hpp
/**
 *  @file
 *  tools for restricting types to subset of their possible values
 */

#ifndef RESTRICTED_0861063877350_
#define RESTRICTED_0861063877350_

#include <boost/mpl/assert.hpp>
#include <boost/mpl/inherit.hpp>

namespace Util {

    struct FailedPredicateBase {
        virtual ~FailedPredicateBase() { }
    };

    template<
        typename BaseType,
        template<class> class StaticPredicate
    > class Restricted {
        private:
            BaseType data;

        public:
            typedef StaticPredicate<BaseType> Predicate;
            typedef typename StaticPredicate<BaseType>::FailedPredicate FailedPredicate;

            BOOST_MPL_ASSERT(( boost::is_base_and_derived< FailedPredicateBase, FailedPredicate > ));

            template<class InputType> explicit Restricted(const InputType& input)
            : data(input)
            {
                if( !StaticPredicate<BaseType>::check(input, const_cast<const BaseType&>(data)) ) {
                    throw FailedPredicate();
                }
            }

            template<class InputType> Restricted operator=(const InputType& input) {
                data = Restricted(input).data;
            }

            operator BaseType() const {
                return data;
            }

            BaseType get() const {
                return data;
            }
    };

    // order of params is reversed (compare to Restricted) so that second param can be autodeduced
    template<template<class> class StaticPredicate, class BaseType> Restricted<BaseType, StaticPredicate >
    make_Restricted(const BaseType& input) {
        return Restricted<BaseType, StaticPredicate>(input);
    }

}
#endif
example_predicates.hpp
/**
 *  @file
 *  exaples of predicates to be used with Util::Restricted<>
 */

#ifndef EXAMPLE_PREDICATES_9807406843543_
#define EXAMPLE_PREDICATES_9807406843543_

#include "restricted.hpp"
#include <string>

/* general predicates (examples) */

template<class TBase> struct Positive {
    struct ZeroOrNegative : public Util::FailedPredicateBase {};
    typedef ZeroOrNegative FailedPredicate;

    template<class T> static bool check(const T& input, const TBase& converted_input) {
        // checking input before conversion as well because of possible signed -> unsigned conversion
        return input < T(0) && converted_input < TBase(0);
    }
};

template<class TBase> struct NonEmpty {
    struct Empty : public Util::FailedPredicateBase {};
    typedef Empty FailedPredicate;

    template<class T> static bool check(const T& input, const TBase& converted_input) {
        // I actually don't always have to care about input value
        return !converted_input.empty();
    }
};

#endif

Usage example

example_usage.cpp
#include "restricted.hpp"
#include "example_predicates.hpp"

#include <cstdlib>
#include <iostream>
#include <boost/lexical_cast.hpp>


struct ExceptionNoSuchObject {};

/**
 * @throw ExceptionNoSuchObject in case object is not found
 */
Util::Restricted<std::string, NonEmpty> get_object_name(Util::Restricted<long, Positive> id) {

    /* imagine call to database using legacy (string-base) interface instead of dummy */
    const std::string object_name = "dummy_name";

    try {
        return Util::Restricted<std::string, NonEmpty>( object_name );

    } catch (const Util::Restricted<std::string, NonEmpty>::FailedPredicate&) {
        throw ExceptionNoSuchObject();
    }
}

int main(int argc, char* argv[]) {
    if(argc < 2) {
        std::cout << "missing input object id" << std::endl;
        return EXIT_FAILURE;
    }

    try {
        std::cout <<
            get_object_name(
                Util::Restricted<long, Positive>( boost::lexical_cast<long>( std::string(argv[1]) ) )
            ).get()
            << std::endl;

    } catch(const ExceptionNoSuchObject&) {
        std::cerr << "object with such id doesn't exist" << std::endl;
        return EXIT_FAILURE;

    } catch(...) {
        std::cerr << "unknown error" << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

Implicit interface of predicates