tags:

views:

129

answers:

4

I'd like to prevent clients of my class from doing something stupid. To that end, I have used the type system, and made my class only accept specific types as input. Consider the following example (Not real code, I've left off things like virtual destructors for the sake of example):

class MyDataChunk
{
    //Look Ma! Implementation!
};

class Sink;

class Source
{
    virtual void Run() = 0;
    Sink *next_;
    void SetNext(Sink *next)
    {
        next_ = next;
    }
};

class Sink
{
    virtual void GiveMeAChunk(const MyDataChunk& data)
    {
        //Impl
    };
};

class In
{
    virtual void Run
    {
        //Impl
    }
};

class Out
{
};

//Note how filter and sorter have the same declaration. Concrete classes
//will inherit from them. The seperate names are there to ensure only
//that some idiot doesn't go in and put in a filter where someone expects
//a sorter, etc.

class Filter : public Source, public Sink
{
    //Drop objects from the chain-of-command pattern that don't match a particular
    //criterion.
};

class Sorter : public Source, public Sink
{
    //Sorts inputs to outputs. There are different sorters because someone might
    //want to sort by filename, size, date, etc...
};

class MyClass
{
    In i;
    Out o;
    Filter f;
    Sorter s;
public:
    //Functions to set i, o, f, and s
    void Execute()
    {
        i.SetNext(f);
        f.SetNext(s);
        s.SetNext(o);
        i.Run();
    }
};

What I don't want is for somebody to come back later and go, "Hey, look! Sorter and Filter have the same signature. I can make a common one that does both!", thus breaking the semantic difference MyClass requires.

Is this a common kind of requirement, and if so, how might I implement a test for it?

+2  A: 

Why wait until runtime? Compile time checks (static asserts, for example) can let the offending code fail to compile.

But to answer your question, I believe no - you cannot do this unless you're running the compiler as part of your test.

But, again, look into compile time assertions or concept checks. There's no reason to wait until test time to see these errors.

Assaf Lavie
Compile time assertions would be fine if I wanted to assert that a statement **would** compile. However, asserting that something does **not** compile is more difficult. I don't particularly care if it's compile time or runtime (I'd assume it'd **have** to be compile time though)
Billy ONeal
If you want to disallow certain things from compiling then the right thing to do is _make_ them fail to compile by introducing various compile time checks (e.g. like concept checks). Doing this in run time is too late.
Assaf Lavie
@Assaf Lavie: The problem there is that I need the rest of the project to compile without failing.
Billy ONeal
A: 

To test for expected compile time failure, you'll need to be running the tests in some kind of framework with automated building. The framework calls the compiler to build from source, check the compile return code for success/failure against expected and then run the resulting tests .exe if it were runtime tests.

I know such features exist in Boost's regression test, although I haven't actually studied how its implemented. Maybe you can have a look there for examples / ideas.

KTC
+3  A: 

In general, no. If a statement doesn't compile (ie. is not valid ISO C++), the compiler is no longer under the obligation to create an executable.

There is one famous exception, which even has an abbreviation: SFINAE, or Substitution Failure Is Not An Error. Roughly speaking, in a number of situation (such as overload resolution) the compiler will instantiate some templates implicitly, to consider the results. It would be very annoying if the compiler would try an instantiation that failed to compile, and then stopped the entire compilation process. Therefore, it will silently discard instantiation errors in such cases.

This SFINAE mechanism can be exploited to determine certain attributes at compile time. Consider this:

template<typename T> class Foo() {
  static char test(T);
  static int test(...);
  static const size_t result = sizeof(test(0));
};

Now, if the expression T(0) compiles, i.e. if 0 can be converted to a T, then Foo::result is sizeof(char)==1 and else Foo::result is sizeof(int).

MSalters
This might be workable. I need to check only a single statement, and I can check Foo::result with an assertion.... +1. Will checkmark if I get something that works.
Billy ONeal
+1  A: 

With the latest clarification the problem is simple:

void foo(Filter*);
void foo(Sorter*);
template<typename T> void test() {
   foo((T*)NULL);
}

If anyone passes a class that's both a Filter and a Sorter, the overload resolution is ambiguous.

MSalters