Topic: All I want for Christmas (was C++0x)
Author: "Andrei Alexandrescu" <andrewalex@hotmail.com>
Date: Sat, 19 May 2001 18:46:07 GMT Raw View
"Paul Mensonides" <pmenso57@home.com> wrote in message
news:153N6.24719$p33.428418@news1.sttls1.wa.home.com...
> template<class T> class SomeCollection {
> private:
> unsigned m_size;
> T* m_v;
> public:
> template<class U, V(...)> void for_each(U (T::* pm)(V), V) {
> for (int i = 0; i < m_size; ++i) {
> m_v[i].*pm(#V); // #V indicates values rather than type
> }
> }
> };
>
> Which, for a given call:
>
> class MyClass {
> public:
> void get(int, int);
> };
>
> void f() {
> SomeCollection<MyClass> x;
> //populate x...
> x.for_each(&MyClass:get, 0, 0); // possible disambiguation of
&MyClass::get,
> maybe: &MyClass::get(int, int) ?
> }
I see... well, if you ask me, quite frankly I don't like it much because the
syntax is a bit convoluted. I mean, by looking at the syntax itself I
couldn't understand your idea. It doesn't mesh nicely with the rest of C++.
However, C++ really cries for a typesafe multiple parameters support in both
function calls and template parameter list. The current varargs mechanism is
a shame. Is there anyone out there with a proposal?
I'm thinking of a compile-time pattern matching for functions with varargs,
allowing them to recurse to their own versions with fewer arguments. For
example:
template <class T> const T& min(const T& lhs, const T& rhs)
{
return lhs < rhs ? lhs : rhs;
}
template <class T, ...> const T& min(const T& lhs, const T& rhs, ...)
{
return min(lhs, min(rhs, ...));
}
What is not obvious above is that the compiler generates multiple versions
of min for the second overload. IOW, "..." acts like a template argument for
min.
The approach above allows a typesafe printf-like implementation:
template <class T> int printf(const char* format, T& obj)
{
...
}
template <class T, ...> int printf(const char* format, T& obj, ...)
{
int result = printf(format, obj);
... figure out how many bytes in format were eaten by the previous
operation...
return result + printf(format + offset, ...);
}
The way the above would work is by reducing the argument list one by one,
and treating each in separation.
> My example may be going for the most-convoluted award, but the idea anyway
could
> be a good one. Transparent use of templated parameter lists. I think
this
> would make some types of generic programming significantly easier.
That would be very interesting. A mechanism that would allow instantiating a
template with any number of arguments and to figure out those arguments from
inside the template... but wait, that would render Loki::Typelist obsolete
:o).
> Thanks for the book, by the way. It was an eye-opener! :)
> (Unfortunately, all that I have to use right now is VC++ :( , so most of
it
> won't compile.)
You're lucky. My current project bans any template use altogether. We're now
struggling to introduce C++-style casts...
Andrei
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "David Abrahams" <abrahams@mediaone.net>
Date: Sat, 19 May 2001 18:48:53 GMT Raw View
"Paul Mensonides" <pmenso57@home.com> wrote in message
news:153N6.24719$p33.428418@news1.sttls1.wa.home.com...
> Hey, Andrei, what do you think of some type of templated parameter list?
For
> example...
>
> template<class T> class SomeCollection {
> private:
> unsigned m_size;
> T* m_v;
> public:
> template<class U, V(...)> void for_each(U (T::* pm)(V), V) {
> for (int i = 0; i < m_size; ++i) {
> m_v[i].*pm(#V); // #V indicates values rather than type
> }
> }
> };
Something like this is on my big-10 list of missing template features. It is
a sad fact of C++ programming that many idioms require the writing of
forwarding functions. Doing this with templates can get really frustrating
when they need overloads to deal with all the different combinations.
Usually, I have to resort to C++ code generation (using, e.g., Python) just
to ensure I don't make any mistakes. I rather like your syntax, too, though
it should be possible to lose the pound sign (why not just declare v of type
V?) and the parens around the ellipsis (they seem to indicate a function).
-Dave
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "David Abrahams" <abrahams@mediaone.net>
Date: Sat, 19 May 2001 19:51:41 GMT Raw View
"Paul Mensonides" <pmenso57@home.com> wrote in message
news:153N6.24719$p33.428418@news1.sttls1.wa.home.com...
> Hey, Andrei, what do you think of some type of templated parameter list?
For
> example...
>
> template<class T> class SomeCollection {
> private:
> unsigned m_size;
> T* m_v;
> public:
> template<class U, V(...)> void for_each(U (T::* pm)(V), V) {
> for (int i = 0; i < m_size; ++i) {
> m_v[i].*pm(#V); // #V indicates values rather than type
> }
> }
> };
Something like this is on my big-10 list of missing template features. It is
a sad fact of C++ programming that many idioms require the writing of
forwarding functions. Doing this with templates can get really frustrating
when they need overloads to deal with all the different combinations.
Usually, I have to resort to C++ code generation (using, e.g., Python) just
to ensure I don't make any mistakes. I rather like your syntax, too, though
it should be possible to lose the pound sign (why not just declare v of type
V?) and the parens around the ellipsis (they seem to indicate a function).
-Dave
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Mensonides" <pmenso57@home.com>
Date: Sun, 20 May 2001 05:31:20 GMT Raw View
"David Abrahams" <abrahams@mediaone.net> wrote in message
news:089101c0e06e$e2494190$1200a8c0@abeast1.com...
|
| "Paul Mensonides" <pmenso57@home.com> wrote in message
| news:153N6.24719$p33.428418@news1.sttls1.wa.home.com...
| > Hey, Andrei, what do you think of some type of templated parameter list?
| For
| > example...
| >
| > template<class T> class SomeCollection {
| > private:
| > unsigned m_size;
| > T* m_v;
| > public:
| > template<class U, V(...)> void for_each(U (T::* pm)(V), V) {
| > for (int i = 0; i < m_size; ++i) {
| > m_v[i].*pm(#V); // #V indicates values rather than type
| > }
| > }
| > };
|
| Something like this is on my big-10 list of missing template features. It is
| a sad fact of C++ programming that many idioms require the writing of
| forwarding functions. Doing this with templates can get really frustrating
| when they need overloads to deal with all the different combinations.
| Usually, I have to resort to C++ code generation (using, e.g., Python) just
| to ensure I don't make any mistakes. I rather like your syntax, too, though
| it should be possible to lose the pound sign (why not just declare v of type
| V?) and the parens around the ellipsis (they seem to indicate a function).
|
| -Dave
The only reason is that I didn't use something like V v, is because "V" is not a
type, it is a list of types. I was just trying to make it clear that this
function takes a variable number of parameters (statically, of course, i.e. not
....) and directly forwards them in a non-named way. Which is why I used a
"for_each" type thing. You could directly refer to arguments inside the
template with pound signs (i.e. V(#1)) or something. Of course, you would have
to instantiate the template with at least that many arguments, but then you
could change the template definition:
template<class U, V(2)> void for_each(U (T::* pm)(V), V); // V(2) means: AT
LEAST 2 arguments
class X {
public:
void f(int, int);
};
//for a call:
void g() {
int x = 0;
float y = 2.0f;
SomeCollection<X> z;
// populate z...
z.for_each(&X::f(int, float), x, y);
}
//
inside "for_each," each type in "V" can be explicitly referred to by V[0]
and each value passed to "for_each" can be referred to by #V[0] (or #V[1],
etc.). That is the reason I used the pound sign, to differentiate between
values passed versus their types. In the "for_each" case, I passed the values
on to some other function directly. If I wanted to access these values directly
I would use the de-typer operator :):
template<class RETTYPE, PARAMLIST(2)> void for_each(RETTYPE (T::*
pM)(PARAMLIST), PARAMLIST) {
// verify arguments:
if (#PARAMLIST[0] < PARAMLIST[0]()) throw int();
if (#PARAMLIST[1] < PARAMLIST[1]()) throw int();
// ^--value----^ ^--type----^ ^--ctor call
// never mind what this "comparison" is supposed to do, just illustrating
this syntax
for (unsigned i = 0; i < m_size; ++i) {
(m_v[i].*pM)(#V);
}
}
for the call in g() above, this would expand too:
void SomeCollection<X>::for_each(void (X::* pM)(int, float), int arg1, float
arg2) {
if (arg1 < int()) throw int();
if (arg2 < float()) throw int();
for (unsigned i = 0; i < m_size, ++i) {
(m_v[i].*pM)(arg1, arg2);
}
}
where:
RETYPE = void (can only be used where "type" void can be, or compile time
error)
V[0] = int
V[1] = float
#V[0] = unnamedArg1 (arg1, in this example)
#V[1] = unnamedArg2 (arg2)
#V = (unnamedArg1, unnamedArg2)
(i.e. functionClosure(arg1, arg2) not sequencing: functionClosure((arg1,
arg2))
T = "class" X
(To Andrei):
I'm not saying this particular "syntax" is the good, just that it seemed
fairly clean, off the top of my head. This is nothing like the varargs
mechanism, as different copies would be instantiated with different parameters
(and numbers of parameters), likewise, this would be thoroughly typesafe and
would mesh fine with the rest of C++. This type of mechanism would be to
explicitly reduce the over- (mis-) use of the recursive templates, not to
mention overloading template functions with variable number of arguments, i.e.
template<class A> void f(A a);
template<class A, class B> void f(A a, B b);
template<class A, class B, class C> void f(A a, B b, C c);
// etc.
speaking of typelists (ala Loki), this type of thing could ITERATE typelist
definitions rather than having #define TYPELIST_XX(type, _VAR_ARGS_)
TypeList<type, TYPELIST_XW(_VAR_ARGS_)> over and over or
TypeList<int, TypeList<float, NullType> > ... etc. etc.
I agree that recursive templates are extremely interesting and powerful, as you
have shown in your book. But for something as oft used as "for_each" like
stuff, this should be directly part of the language.
Along these same lines, C++ should (as part of the template mechanism) have
support for iterating code generation so that stuff like GenScatterHierarchy and
GenLinearHierarchy (which *rely* on good optimization).
Say like (and once again, never mind this specific syntax):
template<V(...)> class AbstractVisitor {
public:
#for_each X in V { // never mind this evil VB-type syntax too :)
virtual void Visit(const V[X]&) { return; }
}
};
then you could have:
template<V(...)> ConcreteVisitor : public AbstractVisitor<V> {
public: // handle images and paragraphs only.
void Visit(const Image& img) {
// handle images
}
void Visit(const Paragraphs& para) {
// handle paragraphs
}
};
This type of thing extends to your compile-time "if" that you mentioned
earlier..
template<bool T> void f() {
#if (T) {
// do something
}
#else {
// do something else
}
}
Once again, I'm referring to an idea (not a specific syntax), and I may be
poorly expressing myself. :(
Paul Mensonides
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Mensonides" <pmenso57@home.com>
Date: Fri, 18 May 2001 07:44:51 GMT Raw View
Hey, Andrei, what do you think of some type of templated parameter list? For
example...
template<class T> class SomeCollection {
private:
unsigned m_size;
T* m_v;
public:
template<class U, V(...)> void for_each(U (T::* pm)(V), V) {
for (int i = 0; i < m_size; ++i) {
m_v[i].*pm(#V); // #V indicates values rather than type
}
}
};
Which, for a given call:
class MyClass {
public:
void get(int, int);
};
void f() {
SomeCollection<MyClass> x;
//populate x...
x.for_each(&MyClass:get, 0, 0); // possible disambiguation of &MyClass::get,
maybe: &MyClass::get(int, int) ?
}
where for_each expands to: // allowing type "void" to be used as a return type
void SomeCollection<MyClass>::for_each(void (MyClass::* pm)(int, int), int
unnamed_1, int unnamed_2) {
for (int i = 0; i < m_size; ++i) {
m_v[i].*pm(unnamed_1, unnamed_2);
}
}
My example may be going for the most-convoluted award, but the idea anyway could
be a good one. Transparent use of templated parameter lists. I think this
would make some types of generic programming significantly easier. (I'll delay
a further tirade about the semantics of overloading operator->*() for
pointer-to-member-function types and how annoying that is.)
What do you think?
Thanks for the book, by the way. It was an eye-opener! :)
(Unfortunately, all that I have to use right now is VC++ :( , so most of it
won't compile.)
Paul Mensonides
| By the way, if I had my way, I'd have "if" on compile-time constants not
| compile the branch that's guaranteed not to execute. Kind of like template
| functions that are not instantiated unless used. That would simplify a lot
| of generic code - now I have to use Int2Type all the time.
Agreed, that is annoying in one-or-the-other-but-not-both situations...
template<class X, bool T> void f() {
X* x = 0;
if (T) {
x = new X(0);
}
else {
x = new X;
}
}
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Andrei Alexandrescu" <andrewalex@hotmail.com>
Date: Tue, 15 May 2001 02:43:46 GMT Raw View
"Marco Dalla Gasperina" <marcodg@home.com> wrote in message
news:LvnL6.9938$la.137783@news1.sttls1.wa.home.com...
> (1) What is the real reason that you can't partially
> specialize member functions without partially specializing
> the class? What do you all think of removing this
> restriction (I can just hear the compiler writers cringe).
No, I think this feature would be relatively easy to implement. Actually it
is the current rule that sets an unnatural exception.
> (2) We have iterator_traits<>, lets have container_traits<>
> too. Things like is_storted() have been wished for by
> more than me I would guess.
Would be great, I needed that. We need compile-time flags, however, NOT
functions. If is_sorted is a function, you cannot say:
template <class Container>
class Widget
{
Container<Widget*> children_;
...
bool IsChild(Widget* p)
{
if (container_traits<Container>::is_sorted())
{
return children_.find(p) != children_.end();
}
return std::find(children_.begin(), children_.end(), p) !=
children_.end();
}
}
but you can implement IsChild if is_sorted is a static constant.
By the way, if I had my way, I'd have "if" on compile-time constants not
compile the branch that's guaranteed not to execute. Kind of like template
functions that are not instantiated unless used. That would simplify a lot
of generic code - now I have to use Int2Type all the time.
Andrei
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Marco Dalla Gasperina" <marcodg@home.com>
Date: Sun, 13 May 2001 11:50:03 GMT Raw View
I haven't seen these features mentioned yet but since
everyone else seems to be venting I want a piece of the
action too...
(1) What is the real reason that you can't partially
specialize member functions without partially specializing
the class? What do you all think of removing this
restriction (I can just hear the compiler writers cringe).
(2) We have iterator_traits<>, lets have container_traits<>
too. Things like is_storted() have been wished for by
more than me I would guess.
(3) Speaking of traits, does anyone else think that traits
are a good idea... except that they're just a way to get
a kind of poor man's compile-time reflection in a language
that doesn't support it? (Does any language support
compile time reflection?)
I once had the off-kilter idea about "compile time exception
handling" which would let the code handle errors about the
code in the code. You could write really strange stuff but
it could turn out to be *really* efficient. (This could tie
in to Mr. T. Olcyk's advanced preprocessor).
Party on.
marco
P.S. Hey Bjarne, everyone's a critic :-)
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]