Variadic templates (explain them, please)

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Variadic templates (explain them, please)

Tommaso Pecorella
Ok, first a disclaimer: I'm quite obtuse when working with dark magic, and variadic templates *are* dark magic.

Now, consider the following code:

template <typename T, typename OBJ, typename R, typename... Ts>
Callback<R,Ts...> MakeCallback (R (T::*memPtr)(Ts...), OBJ objPtr)
{
  return Callback<R,Ts...> (objPtr, memPtr);
}
template <typename R, typename... Ts>
Callback<R,Ts...> MakeCallback (R (*fnPtr)(Ts...))
{
  return Callback<R,Ts...> (fnPtr, true, true);
}

These are 2 variadic templates.

From the compiler point of view, how can it tell which one is which ?
All the types are templated, and the number of arguments is unspecified in both.

Being dumb, I would have expected to have to do something like: make just one template, and in its implementation check if the 1st type is <something> (a functor, for example), and call something, else check that the 1st and the 2nd are something (e.g., a member functor and a memory pointer), etc.

I'm ok to leave these quirks to the compiler, but I guess that the problem we have with python bindings are related to this. Perhaps clang (or whatever one uses) is smart enough to do the right thing, and translating the same into bindings doesn't work, just because the python code can't figure out what to do - because python is interpreted and c++ isn't.

Thoughts ?

T.

--------------------------------------------------------------

Ciò che consente all'uomo di superare i propri demoni, é quando gli pesa il culo per assecondarli.
-- Zerocalcare

--------------------------------------------------------------

Tommaso Pecorella - Ph.D.

Assistant professor
Dpt. Ingegneria dell'Informazione
Università di Firenze

CNIT - Università di Firenze Unit

via di S. Marta 3
50139, Firenze
ITALY

email: [hidden email] <mailto:[hidden email]>
       [hidden email]

phone : +39-055-2758540
mobile: +39-320-4379803
fax   : +39-055-2758570









--------------------------------------------------------------

Arguing with an Engineer is a lot like
                     wrestling in the mud with a pig,
after a couple of hours you realize the pig likes it.

--------------------------------------------------------------

Tommaso Pecorella - Ph.D.

Assistant professor
Dpt. Ingegneria dell'Informazione
Università di Firenze

CNIT - Università di Firenze Unit

via di S. Marta 3
50139, Firenze
ITALY

email: [hidden email]
       [hidden email]

phone : +39-055-2758540
mobile: +39-320-4379803
fax   : +39-055-2758570









Reply | Threaded
Open this post in threaded view
|

Re: Variadic templates (explain them, please)

Stefano Avallone
Tommaso,

> On 3 Jun 2020, at 12:00, Tommaso Pecorella <[hidden email]> wrote:
>
> Ok, first a disclaimer: I'm quite obtuse when working with dark magic, and variadic templates *are* dark magic.
>
> Now, consider the following code:
>
> template <typename T, typename OBJ, typename R, typename... Ts>
> Callback<R,Ts...> MakeCallback (R (T::*memPtr)(Ts...), OBJ objPtr)
> {
>  return Callback<R,Ts...> (objPtr, memPtr);
> }
> template <typename R, typename... Ts>
> Callback<R,Ts...> MakeCallback (R (*fnPtr)(Ts...))
> {
>  return Callback<R,Ts...> (fnPtr, true, true);
> }
>
> These are 2 variadic templates.
>
> From the compiler point of view, how can it tell which one is which ?
> All the types are templated, and the number of arguments is unspecified in both.

The former has two arguments (a pointer to a member function and an “object”), while the latter has just one argument (a pointer to function).

Bests,
Stefano


> Being dumb, I would have expected to have to do something like: make just one template, and in its implementation check if the 1st type is <something> (a functor, for example), and call something, else check that the 1st and the 2nd are something (e.g., a member functor and a memory pointer), etc.
>
> I'm ok to leave these quirks to the compiler, but I guess that the problem we have with python bindings are related to this. Perhaps clang (or whatever one uses) is smart enough to do the right thing, and translating the same into bindings doesn't work, just because the python code can't figure out what to do - because python is interpreted and c++ isn't.
>
> Thoughts ?
>
> T.
>
> --------------------------------------------------------------
>
> Ciò che consente all'uomo di superare i propri demoni, é quando gli pesa il culo per assecondarli.
> -- Zerocalcare
>
> --------------------------------------------------------------
>
> Tommaso Pecorella - Ph.D.
>
> Assistant professor
> Dpt. Ingegneria dell'Informazione
> Università di Firenze
>
> CNIT - Università di Firenze Unit
>
> via di S. Marta 3
> 50139, Firenze
> ITALY
>
> email: [hidden email] <mailto:[hidden email]>
>       [hidden email]
>
> phone : +39-055-2758540
> mobile: +39-320-4379803
> fax   : +39-055-2758570
>
>
>
>
>
>
>
>
>
> --------------------------------------------------------------
>
> Arguing with an Engineer is a lot like
>                     wrestling in the mud with a pig,
> after a couple of hours you realize the pig likes it.
>
> --------------------------------------------------------------
>
> Tommaso Pecorella - Ph.D.
>
> Assistant professor
> Dpt. Ingegneria dell'Informazione
> Università di Firenze
>
> CNIT - Università di Firenze Unit
>
> via di S. Marta 3
> 50139, Firenze
> ITALY
>
> email: [hidden email]
>       [hidden email]
>
> phone : +39-055-2758540
> mobile: +39-320-4379803
> fax   : +39-055-2758570
>
>
>
>
>
>
>
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Variadic templates (explain them, please)

Tommaso Pecorella
From the point of view of the compiler... it doesn't make any difference.

template <typename T, typename OBJ, typename R, typename... Ts>
Vs
template <typename R, typename... Ts>

The only difference I can see is that the 1st has at least 3 arguments, and the second at least 1.
However, if I try to call one with 4 arguments, both the 1st and the 2nd could be be "right".
You'll need to inspect the actual implementation to know what T, OBJ, or R are being used for, and that's something you can do at linking level (I might be wrong).

T.



> On 3 Jun 2020, at 19:13, Stefano Avallone <[hidden email]> wrote:
>
> Tommaso,
>
>> On 3 Jun 2020, at 12:00, Tommaso Pecorella <[hidden email]> wrote:
>>
>> Ok, first a disclaimer: I'm quite obtuse when working with dark magic, and variadic templates *are* dark magic.
>>
>> Now, consider the following code:
>>
>> template <typename T, typename OBJ, typename R, typename... Ts>
>> Callback<R,Ts...> MakeCallback (R (T::*memPtr)(Ts...), OBJ objPtr)
>> {
>> return Callback<R,Ts...> (objPtr, memPtr);
>> }
>> template <typename R, typename... Ts>
>> Callback<R,Ts...> MakeCallback (R (*fnPtr)(Ts...))
>> {
>> return Callback<R,Ts...> (fnPtr, true, true);
>> }
>>
>> These are 2 variadic templates.
>>
>> From the compiler point of view, how can it tell which one is which ?
>> All the types are templated, and the number of arguments is unspecified in both.
>
> The former has two arguments (a pointer to a member function and an “object”), while the latter has just one argument (a pointer to function).
>
> Bests,
> Stefano
>
>
>> Being dumb, I would have expected to have to do something like: make just one template, and in its implementation check if the 1st type is <something> (a functor, for example), and call something, else check that the 1st and the 2nd are something (e.g., a member functor and a memory pointer), etc.
>>
>> I'm ok to leave these quirks to the compiler, but I guess that the problem we have with python bindings are related to this. Perhaps clang (or whatever one uses) is smart enough to do the right thing, and translating the same into bindings doesn't work, just because the python code can't figure out what to do - because python is interpreted and c++ isn't.
>>
>> Thoughts ?
>>
>> T.
>>
>> --------------------------------------------------------------
>>
>> Ciò che consente all'uomo di superare i propri demoni, é quando gli pesa il culo per assecondarli.
>> -- Zerocalcare
>>
>> --------------------------------------------------------------
>>
>> Tommaso Pecorella - Ph.D.
>>
>> Assistant professor
>> Dpt. Ingegneria dell'Informazione
>> Università di Firenze
>>
>> CNIT - Università di Firenze Unit
>>
>> via di S. Marta 3
>> 50139, Firenze
>> ITALY
>>
>> email: [hidden email] <mailto:[hidden email]>
>>      [hidden email]
>>
>> phone : +39-055-2758540
>> mobile: +39-320-4379803
>> fax   : +39-055-2758570
>>
>>
>>
>>
>>
>>
>>
>>
>>
>> --------------------------------------------------------------
>>
>> Arguing with an Engineer is a lot like
>>                    wrestling in the mud with a pig,
>> after a couple of hours you realize the pig likes it.
>>
>> --------------------------------------------------------------
>>
>> Tommaso Pecorella - Ph.D.
>>
>> Assistant professor
>> Dpt. Ingegneria dell'Informazione
>> Università di Firenze
>>
>> CNIT - Università di Firenze Unit
>>
>> via di S. Marta 3
>> 50139, Firenze
>> ITALY
>>
>> email: [hidden email]
>>      [hidden email]
>>
>> phone : +39-055-2758540
>> mobile: +39-320-4379803
>> fax   : +39-055-2758570

--------------------------------------------------------------

The nice thing about standards is that there are so many to choose from.
And if you really don't like all the standards you just have to wait another year until the one arises you are looking for.
-- A. Tanenbaum, "Introduction to Computer Networks"

--------------------------------------------------------------

Tommaso Pecorella - Ph.D.

Assistant professor
Dpt. Ingegneria dell'Informazione
Università di Firenze

CNIT - Università di Firenze Unit

via di S. Marta 3
50139, Firenze
ITALY

email: [hidden email]
       [hidden email]

phone : +39-055-2758540
mobile: +39-320-4379803
fax   : +39-055-2758570









Reply | Threaded
Open this post in threaded view
|

Re: Variadic templates (explain them, please)

Natale Patriciello
On 04/06/20 at 11:27am, Tommaso Pecorella wrote:
> From the point of view of the compiler... it doesn't make any difference.
>
> template <typename T, typename OBJ, typename R, typename... Ts>
> Vs
> template <typename R, typename... Ts>
>
> The only difference I can see is that the 1st has at least 3 arguments, and the second at least 1.
> However, if I try to call one with 4 arguments, both the 1st and the 2nd could be be "right".
> You'll need to inspect the actual implementation to know what T, OBJ, or R are being used for, and that's something you can do at linking level (I might be wrong).


But Tommaso, you're confusing the template parameter pack with the
function parameters.


```
template <typename T, typename OBJ, typename R, typename... Ts>
Callback<R,Ts...> MakeCallback (R (T::*memPtr)(Ts...), OBJ objPtr)
{
  return Callback<R,Ts...> (objPtr, memPtr);
}
```

The above definition of MakeCallback will have two parameters

```
template <typename R, typename... Ts>
Callback<R,Ts...> MakeCallback (R (*fnPtr)(Ts...))
{
  return Callback<R,Ts...> (fnPtr, true, true);
}

```

The above definition of MakeCallback will have only one parameter. The
two here are perfectly distingushable:

`MakeCallback (&Foo);`

vs

`MakeCallback (&MyClass::Foo, ptr_to_myclass);`

I am perfectly able to distinguish both with my near-zero skill as a
compiler, and I'm sure a real compiler will do the job as well.

BTW, the parameter pack is not expanded here, so you can just ignore it,
if it is easier for you to see it.


Nat
Reply | Threaded
Open this post in threaded view
|

Re: Variadic templates (explain them, please)

Natale Patriciello
On 04/06/20 at 02:29pm, Natale Patriciello wrote:

> On 04/06/20 at 11:27am, Tommaso Pecorella wrote:
> > From the point of view of the compiler... it doesn't make any difference.
> >
> > template <typename T, typename OBJ, typename R, typename... Ts>
> > Vs
> > template <typename R, typename... Ts>
> >
> > The only difference I can see is that the 1st has at least 3 arguments, and the second at least 1.
> > However, if I try to call one with 4 arguments, both the 1st and the 2nd could be be "right".
> > You'll need to inspect the actual implementation to know what T, OBJ, or R are being used for, and that's something you can do at linking level (I might be wrong).

I forgot to say that, if it is ambiguous, the compiler would tell you
without compiling. The minimum-version of the problem you're
trying to reduce it to, is the following:

```
#include <iostream>

template <typename T, typename... Ts>
void MyTemplate ()
{
        std::cout << "Selected first " << std::endl;
}

template<typename... Ts>
void MyTemplate ()
{
        std::cout << "Selected second " << std::endl;
}

int main()
{
        MyTemplate<int> ();
        return 0;
}
```

Which fails, because the two versions are valid for the line `MyTemplate<int> ();`

BUT, as in the case of ns-3 MakeCallback function, if you put a
different number of arguments on the two versions, then there is no
ambiguity at all.

HTH,

Nat