tags:

views:

409

answers:

6

Hello!

Is there a way to check at compile time if some class has constructor with certain arguments ? ?

For example:

class foo {
    foo(std::string &s) {
    }
};

I want to check at compile time that constructor with std::string& always defined. Maybe boost provides such functionality ?

+2  A: 

If you really need it, you can add this function:

static void _dummy() { std::string s; foo f(s); }

Without your constructor, the compilation will fail. Note: your constructor is private. If it's on purpose, then _dummy should be inside the class. Otherwise, you can have it outside the class.

Also, you can templatize or even make it a macro, if this happens a lot in your code.

But to tell the truth, it still looks like a hack. Are you sure you need it?

Igor Krivokon
A: 

What you are asking for sounds a lot like a unit test. I would download something like cppunit and integrate it into your compilation.

Any unit tests you write will be built/executed at compile time. See Unit Testing for more information.

Nick Haddad
Unit tests are not really at compile time, and that was very explicitly asked. There are already 2 solutions which prove that's quite possible.
MSalters
Unit Tests aren't a compile time check, but they can be setup to run when you compile your code. This gives you what you are probably looking for anyways, which is a way to validate functional contracts and assumptions about your code. Is there a reason why this must happen at compile time?
Nick Haddad
A: 

If you need this kind of checks, you'll probably need some other compile time checking

I suggest to take a look to boost concept checking library (documentation is here). You can find some documentation, classes and macros that can help you.

Cătălin Pitiș
+2  A: 

If you're trying to check if foo is constructible from a string, you can use boost::is_convertible.

For example:

BOOST_STATIC_ASSERT((boost::is_convertible<std::string, foo>::value));
James Hopkin
+2  A: 

Using Concept check in boost 1.39:

#include <boost/concept_check.hpp>

class foo_c
{
public:
    foo_c(std::string& s)
    {}
};

template<typename T>
class algo_c
{
BOOST_CONCEPT_ASSERT((boost::Convertible<std::string,T>));
public:
    algo_c()
    {}
};

Removing or changing the constructor of foo_c result in the following compile time error:

error C2440: 'initializing' : cannot convert from 'std::string' to 'foo_c'

EDIT: That can be made to work with explicit constructor with a homemade concept check:

template <typename T>
struct HasTheRightConstructor
{
    BOOST_CONCEPT_USAGE(HasTheRightConstructor)
    {
        std::string v;
        T j(v);
    }
};
This method does not work for explicit constructors. It works only for constructors with 1 non-default argument. It does not work, if conversion operator is defined from "std::string" to T
Konstantin
Good point. Edited so that it works with explicit ctor. Don't know how to do to match a precise prototype (can't make answer from MSalters to compile
+5  A: 

The common way to check if a specific function exists is to take its address and assign it to a dummy variable. This is a lot more precise than the tests mentioned so far, because this verifies the exact function signature. And the question was specifically about string& in the signature, so non-const and thus presumably modifying the string.

However, in this case you cannot use the take-the-address-and-assign-it trick: constructors don't have addresses. So, how do you check the signature then? Simply: Befriend it in a dummy class.

template<typename T>
class checkSignature_StringRef {
    friend T::T(string&);
};

This too is a very specific check: it will not even match similar constructors like foo::foo(std::string &s, int dummy = 0).

MSalters
That doesn't compile with VC8: class contains explicit override 'T::{ctor}' but does not derive from an interface that contains the function declaration
note the defect report here: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#215
Johannes Schaub - litb
crash on gcc3! Compile error on gcc4... That method looks good but I'd be happy to know that anybody else made it compile...
anyway, while this is certainly a very clever idea, i'm not sure how you can make use of it with SFINAE: It fails for invalid constructs in the immediate context of type-checking and invalid expressions. "External effects", such as implicit definition of a constructor that would be illformed, or (i would argue) an invalid friend declaration in an instantiated class body, won't be caught by SFINAE. Those are C++1x rules. In C++03, there is an explicit list of cases where an SFINAE failure happens, which doesn't include an invalid friend declaration.
Johannes Schaub - litb
You could use this too: sizeof(T(*(string*)0)) . I think that should cause an SFINAE failure in C++1x . But i don't think it's required to do an sfinae failure with c++03. In any case, C++1x could do that better using concepts anyway :) Read this one: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html
Johannes Schaub - litb
Following http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#215: this method does work on non ctor function thought
Fair point about SFINAE, but my goal was definitely SFIAE ;)And for compilers that trip over the templated friend, there's always the macro option: #define CHECKSIGNATURE_STRINGREF(T) namespace { class CHECKSIGNATURE_STRINGREF_TESTER { friend T::T(string // Except that this macro will fail on qualified-id's
MSalters
This answer doesn't work. If anybody can make any use of it please send compiler version and code example please.