First of all, this might not be in the right discussion group, but I
thought it could be interesting to discuss it here never the less.
I've been looking at ConceptGCC which is a first implementation of
certain features of the upcoming C++0x revision in the GCC compiler,
and especially variadic templates. When I heard of variadic templates,
the first though that popped into my mind was "great, finally,
concatenated argument dispatching". What is that you might ask. Well
the idea is the be able to correctly seperate a list of arguments into
two sublists that perfectly match the requirements of given functions.
For example, given the two following functions
void f0( int );
void f1( int, int );
The idea is to write an object that will have operator()( int, int,
int ) and correctly dispatch these arguments to the two underlying
functions f0 and f1. So I wrote a piece of code :
template< typename F0, typename F1 >
class DoubleFunctionCall;
template< typename Ret0, typename ... Args0, typename Ret1,
typename ... Args1 >
class DoubleFunctionCall<Ret0 (Args0 ...), Ret1 (Args1 ...)>
{
public:
typedef Ret0 (*F0Type)( Args0 ... );
typedef Ret1 (*F1Type)( Args1 ... );
DoubleFunctionCall( F0Type f0, F1Type f1 )
: _M_f0(f0), _M_f1(f1)
{}
void operator()( Args0 && ... args0, Args1 && ... args1 )
{
(*_M_f0)(std::forward<Args0>(args0) ...);
(*_M_f1)(std::forward<Args1>(args1) ...);
}
private:
F0Type _M_f0;
F1Type _M_f1;
};
If you try and compile this code with ConceptGCC, you'll actually get
an error message saying:
error: parameter packs must be at the end of the parameter list
My question is, is this the expected behaviour and especially, why put
such a restriction ?
Thanks in advance for your comments,
Olivier.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Well, the bigger problem here is that you have multiple argument
packs.
How would the compiler know which arguments to put into Args0 and
which to put into Args1?
You can have only one pack. Playing around with use cases, it's fairly
obvious that the end of the argument list is the most useful place for
it. This is consistent with how every other language handles variadic
argument lists - C, Java, C#, Python, ...
Sebastian
> template< typename F0, typename F1 >
> class DoubleFunctionCall;
>
> template< typename Ret0, typename ... Args0, typename Ret1,
> typename ... Args1 >
> class DoubleFunctionCall<Ret0 (Args0 ...), Ret1 (Args1 ...)>
> {
> If you try and compile this code with ConceptGCC, you'll actually get
> an error message saying:
> error: parameter packs must be at the end of the parameter list
>
> My question is, is this the expected behaviour and especially, why put
> such a restriction ?
Yes, this is mandated by the standard.
Actually, it should technically be possible to lever that restriction,
but you can't possibly have two parameter packs in the same template.
This is *exactly* the right discussion group.
[..]
> If you try and compile this code with ConceptGCC, you'll actually get
> an error message saying:
> error: parameter packs must be at the end of the parameter list
>
> My question is, is this the expected behaviour and especially, why put
> such a restriction ?
Yes, this is expected behavior and is consistent with variadic
parameters to a function:
void f( int x, ..., int y ) or int f( int x, ..., int y, ..., int z )
is also not valid.
Proper is: void f( int x, int y, ... ) with a single variadic pack at
the end only.
The reason is to avoid disambiguity; if a prototype of
void f( int x, ..., int y, ..., int z )
was allowed and somebody called f( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );
which of the parameters is assigned to y?
Now while I am thinking about it, a single ... block somewhere in the
middle could be handled also - crop away parameters from both sides
until only the ... part remains, there you are. However, this requires
a more sophisticated parameter stack administration and may loose
binary compatibility to C libraries at little benefit.
best,
Michael
> but you can't possibly have two parameter packs in the same template.
Well it could be possible if you put a different kind of template
parameter between them (the three kinds are type, integral and symbol
parameters).
Or with concepts, you could have SomeConcept... Args0,
SomeConceptThatIsNotARefinementOfSomeConcept Delim, SomeConcept...
Args1
There is at least one use case where
template<typename ... TailReverse, typename HeadReverse>
struct reverse_pkg
;
would be useful. This is where folding an operation over the
package elements needs to be done in reverse order. One application
is calculating the offsets of tuple elements. The offsets of
TailReverse needs to be calculated before the offset for
HeadReverse can be calculated. The workaround is to first
reverse the list and then use:
template<typename Head, typename ... Tail>
struct forward_pkg
;
However, that requires an extra recursive template meta function
invocation which means more compile time.
This workaround is used in aligned_types.2.zip found here:
http://www.boost-consulting.com/vault/index.php?&directory=variadic_templates
Thanks for the answers. I'll use the last post as a reference since
you all have pretty much the same answer to my question.
> Yes, this is expected behavior and is consistent with variadic
> parameters to a function:
> void f( int x, ..., int y ) or int f( int x, ..., int y, ..., int z )
> is also not valid.
> Proper is: void f( int x, int y, ... ) with a single variadic pack at
> the end only.
Well why would we want to preserve consistency with an non type-safe
system. Variadic parameters (VPs) and variadic template parameters
(VTs) might be similar in the idea and use of the ... operator, but
they offer very different possiblities:
- There is no way to get the number of elements in VPs, whereas
sizeof...() allows to get such information for VTs
- VTs allow you to manipulate the arguments they represent in a
generic way whereas VPs make the arguments completely opaque to the
implementer. Given "Args && ... args", I can write code that will be
called on each parameter with the syntax "function(args)..."
> The reason is to avoid disambiguity; if a prototype of
> void f( int x, ..., int y, ..., int z )
> was allowed and somebody called f( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );
> which of the parameters is assigned to y?
I completely agree with you regarding that example, but once again,
VTs represent a specific list of types that are not lost in the body
of the matching code.
template< typename ... Args0, typename Args1 >
void function( Args0 ... args0, int i, Args1 ... args1 ); // this
should not work - template parameter deduction.
I agree there is no way to determine what Args0 and Args1 will
represent in a call such as function(1, 2, 3, 4, 5, 6). On the other
hand, in the code I originally posted, Args0 and Args1 are perfectly
defined at compile time. So no ambiguity is possible.
template< typename F0, typename F1 >
struct Test;
template< typename Ret0, typename ... Args0, typename Ret1,
typename ... Args1 >
struct Test<Ret0 (Args0 ...), Ret1 (Args1 ...)>
{
void function( Args0 ... args0, Args1 ... args1 ); // this
should work - explicit template parameters.
};
If I instantiate this Test template with "Test<void ( int, int ), void
( int )>", Args0 maps <int, int> and Args1 maps <int>. And there is no
possible ambiguity possible. "Args0 && ... args0" will be replaced by
"int && i0, int && i1" and "args0 ..." by "i0, i1". Just the fact that
several VTs can be present in a template is an indication that it
should be possible to differenciate them when template parameter
deduction is not involved, and therefor only in the case of explicit
template parameter definition.
I think it could be a greaat addition to the language to have that
kind of possibility, and it doesn't seem to be complicated to
integrate, neither can I see any reason it shouldn't be. Maybe there
is a specific case where this would be a problem, but I can't seem to
find it.
Thanks,
Olivier Grant.
There are alot of places where "typename ... T" can be declared not at
the end of template parameter lists and still be perfectly valid. Take
the following example :
template< typename Ret0, typename ... Args0, typename Ret1,
typename ... Args1 >
void function( Ret0 (*pf0)( Args0 ... ), Ret1 (*pf1)
( Args1 ... ) )
{
}
This is perfectly valid code that compiles under conceptg++, despite
the fact that I am not sure what you could do with two function
pointers you know nothing about :D My point is, that in cases where
"typename ... T" is perfectly known at compile time with no ambiguity,
you should be able to use it anywhere:
The following code gives two reasons why the compiler should reject
the use of several variadic template parameter lists. This basically
illustrates that template parameter deduction cannot work with several
variadic template lists.
------------------------------------------
// should be rejected by the compiler since Args0 and Args1 are
// deducted by passed arguments to "function"
template< typename ... Args0, typename ... Args1 >
void function( Args0 ... args0, Args1 ... args1 )
{ /* ... */ }
// no way to know what matches Args0 and Args1.
// ambiguity, therefor rejected by the compiler.
function(1, 2, 3, 4, 5);
------------------------------------------
The following code gives an example of where the compiler should NOT
reject the code since the different variadic template parameter lists
are perfectly determined because they do not rely on template
parameter deduction.
------------------------------------------
template< typename ... Types >
struct TList;
template< typename List0, typename List1 >
struct Split;
template< typename ... Args0, typename ... Args1 >
struct Split<TList<Args0 ...>, TList<Args1 ...>>
{
// This Call member function definition is rejected by
// the compiler despite the fact that Args0 and Args1
// are perfectly known and defined.
void Call( Args0 ... args0, Args1 ... args1 )
{ /* manipulate args0 and args1 as you want */ };
};
typedef Split<
TList<int, int>,
TList<char, double>
> MySplitType;
// Args0 and Args1 are perfectly defined at compile time.
// No ambiguity, so should not be rejected by the compiler.
MySplitType::Call(1, 2, 'c', 3.0);
------------------------------------------
Not that in this last piece of code, the definition of the "Split"
structure compiles fine without the "Call" member function, which
demonstrates the case where the compiler is very happy to match two
variadic template parameter lists. And this is my point, the
inconsistency in the way the compiler deals with variadic templates.
Olivier Grant.
Something like this might work (apologies for syntax errors, mistakes
in lambda notation, etc -- I don't have a compiler for this
language!):
// Define currying combinator
template<typename F> struct Curry;
template<typename R, typename Arg0, typename ... Args> struct
Curry<R(Arg0, Args...)>
{
Curry(const std::function<R(Arg0, Args...)> &f) : f(f) {}
Curry<R(Args)> operator()(Arg0 &&first) { return Curry<R(Args)>( []
(Args &&...rest) -> { f(first, rest...) } }
private:
std::function<R(Arg0, Args...)> f;
};
template<typename R, typename Arg0> struct Curry<R<Arg0>>
{
//...
R operator()(Arg0 &&first) { return f(r); }
};
template<typename R, typename ... Args> Curry<R(Args...)> curry(const
std::function<R(Args...)> &f);
// Define uncurrying combinator
template<typename F> struct Uncurry;
// ... likewise ...
template<typename R, typename R2, typename ... Args>
std::function<R(Args...)> forceReturn(const std::function<R2(Args...)>
&f, R &&r) { return [] (Args... &&a) -> { f(a...); return r; } }
//... then ...
auto fAndG = uncurry(curry(forceReturn(f, curry(g))));
That ought to work. There's probably a simpler way, though.
>
> Thanks for your input on this and do not worry, I do not mind the personal
> post.
Thanks Olivier - I'm going to bring this back to the public sector
since apparently my last post didn't make it to the
comp.lang.c++.moderated group since it was cross-posted to the
now-broken comp.std.c++ group.
> I think this would be a great addition to the standard since the
> current limitation seems unjustified and only there to keep a certain
> uniformity with variable function parameter lists inherited from C.
I agree with you.
>
> I don't know if you've posted a link to the thread of com.lang.c++.moderated
> onto comp.std.c++, but this might be a first way to attract attention. I've
> also started writing a formal paper for proposal, but as this is the first
> time I am trying this, I am not sure of what conventions to follow.
Apparently comp.std.c++ is having technical issues and has been down
since almost jan 2008 with little hopes of coming back up - so i did
email Doug Gregor to bring his attention to your thread.
I applaud you for writing a formal proposal - I think the pre-meeting
mailing should be coming out any day now so the sooner you get it to
Doug the better it would be i think (not sure who the correct person
is - might be Alisdair M).
>
> I had another thought on the subject :
>
> Currently, even after allowing several variadic function parameter packs,
> there would be no way to call a funciton f since there is no way to
> explicitly define two variadic template parameter packs supported directly
> by the langage :
>
> template< typename ... T0, typename ... T1 >
> void f( T0 ... t0, T1 ... t1 ) { /* ... */ }
>
> You could always use a simple holder structure :
>
> template< typename ... Types > struct Pack;
>
> But template function partial specialization not being allowed (does C++0x
> intent to change that ?) you still cannot directly call f with the currently
> allowed syntax :
>
> f<explicit types>(); // How can we specify explicitly the two parameter
> packs ?
>
> The only possiblity, being a bite verbose is :
>
> template< typename P0, typename P1 > struct f;
>
> template< typename ... T0, typename ... T1 > struct f<Pack<T0 ...>, Pack<T1
> ...>> {
> static void call( T0 ... t0, T1 ... t1 ) { /* ... */ }
> };
>
> With this code, you could 'call' f and directly specify how arguments should
> be split :
>
> f<Pack<int, int>, Pack<double, char>>::call(1, 2, 3.0, 'c');
>
Yes I wonder if the following should work too (pseudo-codish):
template<int Separator1, class ... P0, int Separator2, class ... P1>
void f(P0 ... p0, P1 ... p1);
f<0,int,int,0,double, char>(3,4,5.0,'c');
The idea is interspersing separator parameters (could be non-type or
template parameters) so that deduction is unambiguous.
I sense that the machinery to implement this would be much like a
regular expression engine.
> Could the language be updated to support declaring an explicit parameter
> pack with the following syntax :
>
> <int, double, char> // Declares a parameter pack with int, double, char.
>From reading the papers, it seems that they've toyed around with that
idea and settled on not implementing this entity directly - but it's
worth suggesting it again in your proposal if you can think of some
compelling use cases.
In the end whether the proposal gets accepted or not, i feel, would
depend on whether we can come up with some compelling use cases.
<snip>
> What are your thoughts ?
I think that they should have a fairly general specification for the
pattern matcher that deduces the packs - so regardless of the context,
if the pack can be deduced unambiguously it should.
Thus I feel the following should also be deducible
template<class ...List> struct get_last; // only gets selected if List
is empty
template<class ...List, class Last> struct get_last<List...,Last> {
typedef Last result; };
get_last<int>::result = int
get_last<int,char>::result = char
get_last<>::result // compile time error
This would make certain types of recursion unnecessary and turn
certain compile time computations from O(n) to O(1) (in number of
template instantiations).
Thus, I also think this should be deducible (assuming the compiler
partitions on the first occurrence by default)
template<class T, class...List> struct remove_first_occurrence;
tempalte<class T, class ... Partition1, class ... Partition2> struct
remove_first_occurrence<Partition1...,T,Partition2...>
{
typedef Pack<Parition1..., Partition2...> wrapped_result;
};
remove_first_occurence<int, char, char, int, int,
double>::wrapped_result = Pack<char,char,int,double>
I fear that the committee is going to think this is going to be
introducing unnecessary complexity without proportional benefit. I
unfortunately have not been doing much c++ programming of late - but I
can see such a general mechanism of being of significant benefit to
the meta-programming community - since, if my memory serves me right,
this style of pattern matching can be used quite successfully in the
context of the ML programming language in implementing algorithms
elegantly.
I haven't heard from Doug (and I don't know him personally - so I
wouldn't be surprised if I am being spam-filtered by him ;) but
hopefully he will respond to your thread.
I will cross-post this to comp.lang.c++.moderated - so if the
committee members do get a chance to read this thread they can comment
on some of our thoughts.
thanks,
Faisal Vali, MD MSc.
Loyola Radiation Oncology.
>
> Thanks once again for the feedback.
>
> Olivier Grant.
>
> On Fri, May 16, 2008 at 8:47 PM, Faisal Vali <fai...@gmail.com> wrote:
>>
>> Sorry for the personal post Oliver - I posted this to the newsgroup
>> too - but this way you get a heads up on posting in the comp.std.c++
>> group and sending an email to douglas gregor to get his input on it.
>>
>> On May 15, 12:57 pm, Olivier <olivier.gr...@gmail.com> wrote:
>> <snip>
>> >
>> > template< typename F0, typename F1 >
>> > struct Test;
>> >
>> > template< typename Ret0, typename ... Args0, typename Ret1,
>> > typename ... Args1 >
>> > struct Test<Ret0 (Args0 ...), Ret1 (Args1 ...)>
>> > {
>> > void function( Args0 ... args0, Args1 ... args1 );
>> > };
>> >
>>
>> You make a very good point Olivier.
>> Your example should work and the parameters should be unambiguously
>> deducible.
>> Thus the syntax should be allowed in the setting of a partial
>> specialization as long as there is no ambiguity in deduction of the
>> packs.
>> I have not thought about this much - but i can not think of a case
>> where multiple packs would be appropriate in a primary template
>> definition (although some function template definitions might allow
>> disambiguation of them).
>>
>> (pseudo-code warning)
>> template <class R1, class R2, typename ...A1, typename ...A2>
>> auto compose_function_pointers(R1 (*f1)(A1...), R2 (*f2)(A2...)) ->
>> result<R1(A1),R2(A2)>::type
>> {
>> ...
>> }
>>
>> I suspect that this should not be that difficult to implement - but
>> then I thought polymorphic lambdas (type-parameterized lambdas) and
>> local templates should not be that challenging and it appears they
>> are, based on the fact that the committee is passing on them.
>> Keep in mind I have no experience developing compilers.
>>
>> Either way, there is a much better newsgroup to post such queries to:
>> comp.std.c++ (I have tried to cross-post this) where they tend to
>> focus more on aspects of the syntax, grammar and semantics of the
>> language and its evolution.
>>
>> I would hurry and try and at least get it to Douglas Gregor's
>> (<dgregor//AT\\osl//dot\\iu//dot\\edu> - replace//dot\\=. and //AT\
>> \=@) attention asap - the C++ committee will be meeting in early-mid
>> june - perhaps they can discuss your case.
>>
>> I hope you can convince them in time :)
>>
>> thank you,
>> Faisal Vali, MD MSc.
>> Loyola Radiation Oncology.