wrote:
Yep. Anyway, I have to include one specific header file anyway which
contains my own type definitions, such as
typedef float real;
which I can use to decide if I wan't to use float or doubles in my
library. There I can include the implementation:
At first sight, I would say that *generic* structures like Identity
(or macros like yours) and *specific-to-the-project* typedefs like
the one above should not belong to the same header file. I suppose
there are specific cases where it might be ok though, but generally
it's not a clean design.
#define PASTEL_NO_DEDUCTION(x) typename Pastel:
arenthesesRemover<void
(x)>::Type
namespace Pastel
{
template <typename T>
class ParenthesesRemover
{
};
template <typename T>
class ParenthesesRemover<void (T)>
{
public:
typedef T Type;
};
}
If you think you have a clearer interface using this macro, then I
guess this solution is perfectly ok, especially if you use it in a one-
man/small-team project. However, a macro, leaving the usual drawbacks
aside, hides the real expression to a reader of your code. Whether it
is good or not is a matter of opinion. I tend to believe the usage of
a macro in C++ is justified mainly in 4 situations :
- the substituted expression is complex / long / hard to understand
- the name of the macro is very widely known (e.g. STATIC_ASSERT) so
that its signification is clear to everyone
- taking care of issues about a specific compiler/environnment on a
portable project
- preprocessor metaprogramming
The first situation might be applied to your macro. But, of course, as
I said, that's a matter of opinion.
However, this is intrusive
The "instrusive" argument does not stand since we have the control of
the class definition.
and in principle this information does not
belong in the class.
I don't see why it should not belong to the class. It is precisely a
direct property of the class type.
I think it works out nice. To demonstrate, my current library has 362
uses of the macro PASTEL_NO_DEDUCTION as given. Let us pick some
examples:
template <int N, typename Real>
Vector<N, Real> refract(
const Vector<N, Real>& from,
const Vector<N, Real>& normal,
const PASTEL_NO_DEDUCTION(Real)& fromIndex,
const PASTEL_NO_DEDUCTION(Real)& toIndex);
template <typename Type>
ConstantColorImageSampler<Type> constantColorImageSampler(
const PASTEL_NO_DEDUCTION(Type)& color);
template <int N, typename Image_View>
void diamondElement(
const View<N, bool, Image_View>& image,
const PASTEL_NO_DEDUCTION((Vector<N, real>))& diameter);
As you can see its uses are frequent and varied. Sometimes there is no
type to attach the type into: in this case the more generic alternative
is required.
Yes, however those uses are outside the problem you described in your
article. Again that's perfectly ok for a specific project.
Nonetheless, as a general pattern for the problem you described, the
type trait solution seems to be smoother than a generic solution that
requires an external structure and a macro.
Alexandre Courpron.