Reputation: 106609
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?
Upvotes: 3
Views: 197
Reputation: 180245
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.
Upvotes: 1
Reputation: 180245
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).
Upvotes: 3
Reputation: 9003
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.
Upvotes: 0
Reputation: 76113
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.
Upvotes: 2