Types through a macro

K

Kaba

Hello,

Assume:

template <typename A, typename B>
class C
{
};

#define MACRO(x) x

template <typename D>
class E
{
};

What I'd like to do is essentially the following:

E<MACRO(C<int, int>)> e;

However, now the comma inside the macro is interpreted as separating
macro parameters which results in an error. Ok, let us try to add
parentheses:

E<MACRO((C<int, int>))> e;

This get's rid of the earlier error but brings another one: the
declaration

E<(C<int, int>)> e;

is not legal C++.

Is there a way around?

Aside, it seems that
(C<int, int>) c;

is legal. What else can you do with parenthesized types?
 
A

Andrey Tarasevich

Kaba said:
What I'd like to do is essentially the following:

E<MACRO(C<int, int>)> e;

However, now the comma inside the macro is interpreted as separating
macro parameters which results in an error. Ok, let us try to add
parentheses:

E<MACRO((C<int, int>))> e;

This get's rid of the earlier error but brings another one: the
declaration

E<(C<int, int>)> e;

is not legal C++.

Is there a way around?

There is, but it is ugly to the point of being virtually unusable

#define ARGS C<int, int>
E<MACRO(ARGS)> e;
#undef ARGS
 
K

Kaba

Kaba said:
Is there a way around?

Oh, I came up with a solution:

#define DEDUCTION(x) typename Deductor<void ##x>::Result

template <typename Type>
class Deductor
{
};

template <typename Type>
class Deductor<void (Type)>
{
public:
typedef Type Result;
};

template <int A, typename B>
class A
{
};

template <typename C>
class B
{
};

int main()
{
B<DEDUCTION((A<1, int>))> a;

return 0;
}
 
K

Kaba

blargg said:
Kaba said:
Oh, I came up with a solution:

#define DEDUCTION(x) typename Deductor<void ##x>::Result
[...]

Why ##x instead of just x? Otherwise, clever solution to passing typenames
with commas in them to a macro!

Thanks:) You are right, I removed it just after posting. Additionally, I
noticed that you can wrap the x into extra parentheses so that you need
not use double parentheses for simple types:

#define DEDUCTION(x) typename Deductor<void (x)>::Result

B<DEDUCTION((A<1, int>))> a;
B<DEDUCTION(int)> a;

I actually wrote this thing down here:

http://kaba.hilvi.org/Programming_C++/Texts/Templates_Macros.htm

The thing that motivates me to search for this kind of solution is this:

http://kaba.hilvi.org/Programming_C++/Texts/Restricted_Deduction.htm
 
K

Kaba

Gert-Jan de Vos said:
Why not just:

typedef C<int, int> MyC;
E<MACRO(MyC)> e;

Because you have to be able to do it in a function declaration.. See the
link in my other post.
 
C

courpron

The thing that motivates me to search for this kind of solution is this:

http://kaba.hilvi.org/Programming_C++/Texts/Restricted_Deduction.htm

I've quickly checked your article about restricted deduction.
It seems to me that there is a simple solution using type traits, that
resolves the problems you described.

From your "attempt 1 : direct templatization" :

template <typename Type>
class B
{
public:
B();

B<Type>& operator+=(Type value);
B<Type> operator+(Type value) const;

typedef Type type_trait;

private:
Type value_;
};

template <typename Type>
B<Type>::B()
: value_(0)
{
}

template <typename Type>
B<Type>& B<Type>::eek:perator+=(Type value)
{
value_ += value;
return *this;
}

template <typename Type>
B<Type> B<Type>::eek:perator+(Type value) const
{
B<Type> copy(*this);
copy += value;
return copy;
}

template <typename Type>
B<Type> operator+(typename B<Type>::type_trait value, const B<Type>&
that)
{
// Assuming symmetric addition
return that + value;
}

int main()
{
B<float> b;

b = b + 1;
b = 1 + b;

return 0;
}


Alexandre Courpron.
 
K

Kaba

wrote:
I've quickly checked your article about restricted deduction.
It seems to me that there is a simple solution using type traits, that
resolves the problems you described.

Yep, it is essentially the attempt 4 on the article. However, using a
separate Identity metafunction is more generic.
 
C

courpron

 wrote:


Yep, it is essentially the attempt 4 on the article.

Yes, I read too quickly and I didn't see that attempt. It is indeed
rather similar to what I proposed.
However, using a separate Identity metafunction is more generic.

Question is : " does we really need this generecity, since we control
the declaration of the class ?"

Generally, you use the identity function from a library, boost for
example (otherwise the "identity" solution is obviously the worst one
since you have to declare the identity structure each time).

So the identity solution requires :
-including the header :
#include "...."

- taking care of the namespace of identity :
boost::mpl::identity<...>


On the other hand, the type_trait solution just need a nested
typedef :
typedef Type type_trait;

Since a typedef is as short and as simple as an inclusion of a file,
generecity doesn't bring here anything in terms of DRY (don't repeat
yourself) ; except if you already include that header because, by
chance, your class needs elsewhere the identity structure or any other
structure present in the header.

It seems that, with the identity solution, you have to write more
code, you depend on an external structure and on an (additional)
library, and you put more stress on the compiler.

Note that this can be more of a general debate about the (abusive) use
of generic code.


Alexandre Courpron.
 
K

Kaba

wrote:
Question is : " does we really need this generecity, since we control
the declaration of the class ?"

Generally, you use the identity function from a library, boost for
example (otherwise the "identity" solution is obviously the worst one
since you have to declare the identity structure each time).

So the identity solution requires :
-including the header :
#include "...."

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:

#define PASTEL_NO_DEDUCTION(x) typename Pastel::parenthesesRemover<void
(x)>::Type

namespace Pastel
{

template <typename T>
class ParenthesesRemover
{
};

template <typename T>
class ParenthesesRemover<void (T)>
{
public:
typedef T Type;
};

}
On the other hand, the type_trait solution just need a nested
typedef :
typedef Type type_trait;

However, this is intrusive and in principle this information does not
belong in the class.
It seems that, with the identity solution, you have to write more
code, you depend on an external structure and on an (additional)
library, and you put more stress on the compiler.

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.
 
C

courpron

 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::parenthesesRemover<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.
 
K

Kaba

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.

The reason is as you state. The problem is that 'typename Identity
<Type>::Result' (or a more suggestive 'typename NoDeduction
<Type>::Result') would clutter my interfaces unreadable (to my eyes).
For the most part I avoid macros as far as possible, but here I can't
see a way to make the syntax nice without a macro. On the other hand,
it's not that bad since clashing the name of the macro is pretty
improbable. Can you see other options?
The "instrusive" argument does not stand since we have the control of
the class definition.


I don't see why it should not belong to the class. It is precisely a
direct property of the class type.

We could be talking of different situations here. The problem in the
article is just one instance of the general problem of restricting
template parameter deduction. In general, how you want the template
deduction to work for each function should not relate to what types you
work with.
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.

I am not sure what you refer to with the problem I described. To me the
general problem is to restrict template parameter deduction. If you mean
the specific problem given in the article, in isolation, then you might
be right. However, when we extend the technique over the whole library
then your technique does not, in my opinion, generalize in a practical
manner.
 
C

courpron

[...]
The reason is as you state. The problem is that 'typename Identity
<Type>::Result' (or a more suggestive 'typename NoDeduction
<Type>::Result') would clutter my interfaces unreadable (to my eyes).
For the most part I avoid macros as far as possible, but here I can't
see a way to make the syntax nice without a macro. On the other hand,
it's not that bad since clashing the name of the macro is pretty
improbable. Can you see other options?

Another option to make the interface clearer ?
Well no, at least not in C++03. In fact the construct "typename
my_struct<T>::type" is a pretty basic one and sometimes the only
purpose of structures like my_struct is to encapsulate a more complex
expression in order to give a simpler interface.

We could be talking of different situations here. The problem in the
article is just one instance of the general problem of restricting
template parameter deduction. In general, how you want the template
deduction to work for each function should not relate to what types you
work with.

Yes I think we are talking about different problems. See below.
I am not sure what you refer to with the problem I described.

The problem I'm referring to is the one you stated at the beginning of
your article :

"The problem is to make a generalization of class A, a class template
D, that can use float, or any other type Type as the underlying type,
and that works for float exactly like the given class A. "

This is already a general problem, and the type trait solution is a
simple pattern to use, that depends on nothing, and is flexible ( i.
e., if it's needed, you can also use a custom type trait instead of
the straight template parameter of the class, depending on the class
specializations, etc.).

The problem you are referring to concerns the capability of putting an
arbitrary type in a non-deduced context. In that case, you indeed need
a generic and non-intrusive solution.


Alexandre Courpron.
 
C

Chris M. Thomasson

Kaba said:
Hello,

Assume:

template <typename A, typename B>
class C
{
};

#define MACRO(x) x

template <typename D>
class E
{
};

What I'd like to do is essentially the following:

E<MACRO(C<int, int>)> e;

However, now the comma inside the macro is interpreted as separating
macro parameters which results in an error. Ok, let us try to add
parentheses:

E<MACRO((C<int, int>))> e;

This get's rid of the earlier error but brings another one: the
declaration

E<(C<int, int>)> e;

is not legal C++.

Is there a way around?

Aside, it seems that
(C<int, int>) c;

is legal. What else can you do with parenthesized types?


what about something simple like:


#define INVOKE(X) X()

template<typename A, typename B>
class C {
};

template<typename D>
class E {
};

#define TYPE1() C<int, int>
#define TYPE2() C<char, char>
#define TYPE3() C<short, int>

E<INVOKE(TYPE1)> e1;
E<INVOKE(TYPE2)> e2;
E<INVOKE(TYPE3)> e3;

int main() {
return 0;
}


?
 
C

Chris M. Thomasson

[...]

Ahhh, nevermind. That's not what you want. I should have read the thread.

;^(...
 
C

Chris M. Thomasson

Chris M. Thomasson said:
[...]

Ahhh, nevermind. That's not what you want. I should have read the thread.

;^(...

Humm... What about something really odd like:


#define CONCAT_X(T1, T2)T1##T2
#define CONCAT(T1, T2)CONCAT_X(T1, T2)


#define PLACE_1(X) X
#define PLACE_2(X1, X2) X1, X2
#define PLACE_3(X1, X2, X3) X1, X2, X3
#define PLACE_4(X1, X2, X3, X4) X1, X2, X3, X4
#define PLACE(D, X)CONCAT(PLACE_, D)X


template<typename A, typename B>
class C {
};


template<typename D>
class E {
};


E<PLACE(2, (C<int, short>))> e1;
E<PLACE(2, (C<char, short>))> e2;
E<PLACE(2, (C<short, long>))> e3;


int main() {
return 0;
}



lol... ;^D
 
T

Triple-DES

Oh, I came up with a solution:

[Fixed trivial error and changed the macro per blargg's suggestion]
#define DEDUCTION(x) typename Deductor<void x>::Result

template <typename Type>
class Deductor
{

};

template <typename Type>
class Deductor<void (Type)>
{
public:
typedef Type Result;

};

template <int N, typename B>
class A
{

};

template <typename C>
class B
{

};

int main()
{
B<DEDUCTION((A<1, int>))> a;

return 0;

}

This expands to:

int main()
{
B<typename Deductor<void (A<1, int>)>::Result> a;

return 0;
}

Here, the keyword "typename" is not allowed, since main is not a
template. However, the "typename" is required if Result was in fact a
dependent name to use the DEDUCTION macro in a context where typename
was required, for instance in:

template<typename T>
void f()
{
B<typename Deductor<void (T)>::Result> a;
}

Note that this is not merely a formality. In fact, gcc rejects your
example, and is right to do so.
 
T

Triple-DES

template<typename T>
void f()
{
  B<typename Deductor<void (T)>::Result> a;

}

Very silly example, of course, since there is no comma in "T" :)

Consider instead:
B<DEDUCTION((A<1, T>))> a; // T could be a template-parameter or
simply a typedef

A possible solution is to omit "typename" from the macro and instead
write:

B<typename DEDUCTION((A<1, T>))> a;

if T is a template parameter.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,818
Latest member
SapanaCarpetStudio

Latest Threads

Top