Hello,
I work on big software project that is ported to several
platforms/compilers,
including Linux,Solaris/GCC 2.95.2,3.0.4); Windows/MSVC 6.0;
AIX/VisualAge; MacOS X / GCC 2.95.2 (Apple).
Recently, I decided to make use of Boost library (v 1.28) and
the first thing I tried was Boost Concept Check Library (BCCL).
Unfortunately, the use of the library wasn't as smooth as I expected,
namely, with respect to BOOST_CLASS_REQUIRES macro.
Thus, I'd like to submit a couple of observations
and one proposal regarding usage of this macro.
Let's start with observations:
following is the snippet from header:
[code
// The BOOST_CLASS_REQUIRES macros use function pointers as
// template parameters, which VC++ does not support.
#if defined(BOOST_NO_FUNCTION_PTR_TEMPLATE_PARAMETERS)
#define BOOST_CLASS_REQUIRES(type_var, concept)
....
#else
#define BOOST_CLASS_REQUIRES(type_var, concept) \
typedef void (concept ::* func##type_var##concept)(); \
....
end code]
As you could see, BOOST_CLASS_REQUIRES macros evaluate to nothing
if BOOST_NO_FUNCTION_PTR_TEMPLATE_PARAMETERS symbol is defined.
But, to my great surprise, I didn't find any more references to
BOOST_NO_FUNCTION_PTR_TEMPLATE_PARAMETERS symbol throughout the Boost
library.
Moreover, the above comment about VC++ not supporting function
pointers
as template parameters is wrong (at least for MSVC 6.0 with SP5).
Not to be unfounded, I will provide following sample program
successfully compiled and run under MSVC 6.0 with SP5:
[code
#include <iostream>
using namespace std;
typedef void(*FP)();
template <FP T>
class X {
FP fp_;
public:
X() : fp_(T) {}
void print() const { cout << fp_ << endl; }
};
void f() {}
int main(int, char**) {
X<&f> x1;
x1.print();
return 0;
}
end code]
The same is true for pointers to member functions
of both ordinary and template classes.
OK, this observation could be treated as "cosmetic" one,
but the following won't.
Let's look at the definition of BOOST_CLASS_REQUIRES macro:
[code
#define BOOST_CLASS_REQUIRES(type_var, concept) \
typedef void (concept ::* func##type_var##concept)(); \
template \
struct concept_checking_##type_var##concept { }; \
typedef concept_checking_##type_var##concept< \
BOOST_FPTR concept ::constraints> \
concept_checking_typedef_##type_var##concept
end code]
and at a typical usage of this macro (taken from Boost documentation):
[code
template <class T>
struct generic_library_class
{
BOOST_CLASS_REQUIRES(T, EqualityComparableConcept);
// ...
};
end code]
Here, the "EqualityComparableConcept" is a concept checking class
defined in the "boost" namespace.
OK, that's great and works fine until "generic_library_class" is also
declared in the "boost" namespace.
Let's now suppose "generic_library_class" is declared in
namespace "ns1".
Then, BOOST_CLASS_REQUIRES macro evaluates to following:
[code
namespace ns1
{
template <class T>
struct generic_library_class
{
typedef void (EqualityComparableConcept <T>::*
funcTEqualityComparableConcept)();
...
};
}
end code]
Did you see the problem places?
Yes, it's where "concept" parameter of the macro is substituted
for "EqualityComparableConcept". This name is not declared in the
namespace "ns1"!
The first itch could be to specify "boost::EqualityComparableConcept"
as a value of the second parameter of the macro, but, "not so fast,
Skywalker!" -
in this case we will end up
with "funcTboost::EqualityComparableConcept" name
for the pointer to member function that is not a legal name of C/C++
identifier.
Someone could say "What a big deal?! Just write 'using namespace
boost;' or
'using boost::EqualityComparableConcept;' just before
declaring 'generic_library_class'.".
Well, the declaration of "generic_library_class" seems to be in the
header, right?
And what do you think about writing "using-directives" in header
files?
Yes, right, - it's evil! Especially for large production code. Just
check your favorite C++ book.
Though, the use of "using-declarations" in headers is a moot point,
I also consider it as evil for the same reasons as for "using-
directives".
Anyway, "using-declarations" won't help us, - they just don't work
under MSVC 6.0 in our case.
So, what do we have, - "exclusive circle"? I hope not.
Finally, I got to the proposal that should help us to eliminate
defects
of the macro and to obviate above-stated difficulties.
What if to add one more parameter, say, "ns", to the
BOOST_CLASS_REQUIRES macro
to pass the name of the namespace the concept checking class belongs
to?
Let's look at the new definition of the macro:
[code
#define BOOST_CLASS_REQUIRES(type_var, ns, concept) \
typedef void (ns::concept ::* func##type_var##ns##concept)
(); \
template \
struct concept_checking_##type_var##ns##concept { }; \
typedef concept_checking_##type_var##ns##concept< \
BOOST_FPTR ns::concept ::constraints> \
concept_checking_typedef_##type_var##ns##concept;
end code]
and at it's usage:
[code
namespace ns1
{
template <class T>
struct generic_library_class
{
BOOST_CLASS_REQUIRES(T, boost, EqualityComparableConcept);
// ...
};
}
end code]
that will evaluate to the following:
[code
namespace ns1
{
template <class T>
struct generic_library_class
{
typedef void (boost::EqualityComparableConcept <T>::*
funcTboostEqualityComparableConcept)();
...
};
}
end code]
That's it.
Moreover, this solution does not suffer from yet another drawback
inherent to the original one,
viz, name clashing when using in the same class
concept checking classes with the same name, but from different
namespaces.
The only issue I could see with my suggestion was impossibility to
use concept checking classes
declared at the global scope, but, I hope nobody would think of such
thing, because it's evil
(sorry for repetition).
I hope the proposed solution will increase usability of the BCCL.
Best regards,
Igor Bashkirov.