pure virtual template...

M

mathieu

Hi there,

I don't think I'll be able to describe my issue correctly, so
instead I'll just give a pseudo C++ code I am struggling with.
Basically I am looking for a 'pure virtual template' function that I
would be able to declare in the base class (*).

Thanks for suggestions,
-Mathieu


(*)
struct Base
{
};

struct A : public Base
{
template <typename T> void foo() {}
};

struct B : public Base
{
template <typename T> void foo() {}
};

int main(int argc, char *argv[])
{
Base * base;
if( argc )
{
base = new A;
}
else
{
base = new B;
}
base->foo<int>();
return 0;
}
 
M

mathieu

Hi there,

I don't think I'll be able to describe my issue correctly, so
instead I'll just give a pseudo C++ code I am struggling with.
Basically I am looking for a 'pure virtual template' function that I
would be able to declare in the base class (*).

Hum...without much satisfaction here is my temporary solution.
Hopefully you guys will suggest something not involving making the
base class be polymorphic...


#include <iostream>

struct Base
{
template <typename T> void foo() const;
int base;
virtual void bla() {}
};

struct A : public Base
{
template <typename T> void foo() const { std::cout << "A" <<
std::endl; }
double a;
};

struct B : public Base
{
template <typename T> void foo() const { std::cout << "B" <<
std::endl; }
float b;
};

template <typename T>
void Base::foo() const
{
const A * a = dynamic_cast<const A*>(this);
const B * b = dynamic_cast<const B*>(this);
if( a ) a->foo<T>();
else if( b ) b->foo<T>();
}

int main(int argc, char *argv[])
{
Base * base;
if( argc > 1 )
{
base = new A;
}
else
{
base = new B;
}
base->foo<int>();
return 0;
}
 
S

Stuart Redmann

mathieu said:
Hi there,

I don't think I'll be able to describe my issue correctly, so
instead I'll just give a pseudo C++ code I am struggling with.
Basically I am looking for a 'pure virtual template' function that I
would be able to declare in the base class (*).

As most compilers implement virtual method by means of virtual method tables,
declaring a template member virtual is not allowed (sorry that I cannot cite the
paragraph of the C++ standard). Were it not so, the size of the VMT table could
not be determined by the compiler (it cannot predict how many instantiations of
the template member could be needed in the following code). Thus templates and
virtual methods don't go together.
> (*)
> struct Base
> {
> };
>
> struct A : public Base
> {
> template <typename T> void foo() {}
> };
>
> struct B : public Base
> {
> template <typename T> void foo() {}
> };
>
> int main(int argc, char *argv[])
> {
> Base * base;
> if( argc )
> {
> base = new A;
> }
> else
> {
> base = new B;
> }
> base->foo<int>();
> return 0;
> }
>

I think you mixed up two more or less orthogonal concepts of the C++ programming
language: templates are used to reuse code where the logic is known at compile
time (that means you know exactly what your code will do when you compile the
code), whereas virtual methods are used to reuse code whose semantics will be
determined at run-time. If you know at compile time that each class A and B will
have a foo implementation with template parameter int, you may do the following:

struct Base
{
virtual void foo_int () = 0;
};

struct [A and likewise B] : public Base
{
template <typename T> void foo() {}
virtual void foo_int ()
{
foo<int> ();
}
};

int main(int argc, char *argv[])
{
:
base->foo_int();
return 0;
}

Note that there are two different resolution mechanisms involved: template
programming uses the determination with instantiation of a template function
should be used (the proper template argument is deduced), whereas virtual
methods employ a resolution mechanism involving VMTs: this table decides at
run-time which method should be invoked. You are trying to get both mechansims
working in a single step. For above mentioned reasons, this won't work.

Regards,
Stuart
 
J

Jonathan Lane

Hum...without much satisfaction here is my temporary solution.
Hopefully you guys will suggest something not involving making the
base class be polymorphic...


I have to say, I don't really see the problem or what you're hoping to
achieve. I have a suspicion that you want to dynamically invoke a
child member function without knowing the child type. That's what
virtual functions are for. You might also take a look at some design
patterns maybe the command or visitor pattern would help. But, as I
say, I don't really get what you're after here. Maybe a little more
clarification is required.
 
M

Maarten Kronenburg

"mathieu" wrote in message
Hi there,

I don't think I'll be able to describe my issue correctly, so
instead I'll just give a pseudo C++ code I am struggling with.
Basically I am looking for a 'pure virtual template' function that I
would be able to declare in the base class (*).

Thanks for suggestions,
-Mathieu

Mathieu,
In The C++ Programming Language, Stroustrup (3rd ed.), in 13.6.2 (page 348),
it says:
"A member template cannot be virtual."
and there follows an explanation.
For more on virtual functions see for example:
http://www.parashift.com/c++-faq-lite/virtual-functions.html
Virtual functions are used to make a class runtime (or dynamic) polymorphic.
For a proper runtime polymorphic class you may use the NVI (NonVirtual
Interface) design pattern, see
C++ Coding Standards, Sutter & Alexandrescu.
You may also check out my runtime polymorphic integer class, see N2143 on:
http://open-std.org/jtc1/sc22/wg21/docs/papers/2007/#mailing2007-01
Of N2143 there will be a third revision in the near future.
Regards, Maarten.
 
D

dviken

Hum...without much satisfaction here is my temporary solution.
Hopefully you guys will suggest something not involving making the
base class be polymorphic...

Not sure exactly what you are trying to achieve, but this works well
with template classes instead of template functions.
It does use polymorphism, but that is just another way of defining an
interface:

#include <iostream>

template <typename T>
struct Base
{
virtual void foo(T x) = 0;
};

template <typename T>
struct A: Base<T>
{
void foo(T x) { std::cout << "A=" << x << "\n"; }
};

template <typename T>
struct B: Base<T>
{
void foo(T x) { std::cout << "B=" << x << "\n"; }
};

int main(int argc)
{
Base<int> *base;
if (argc > 1)
base = new A<int>;
else
base = new B<int>;
base->foo(45);
return 0;
}
 
M

mathieu

"mathieu" wrote in message



Mathieu,
In The C++ Programming Language, Stroustrup (3rd ed.), in 13.6.2 (page 348),
it says:
"A member template cannot be virtual."
and there follows an explanation.
For more on virtual functions see for example:http://www.parashift.com/c++-faq-lite/virtual-functions.html
Virtual functions are used to make a class runtime (or dynamic) polymorphic.
For a proper runtime polymorphic class you may use the NVI (NonVirtual
Interface) design pattern, see
C++ Coding Standards, Sutter & Alexandrescu.
You may also check out my runtime polymorphic integer class, see N2143 on:http://open-std.org/jtc1/sc22/wg21/docs/papers/2007/#mailing2007-01
Of N2143 there will be a third revision in the near future.
Regards, Maarten.

Pretty cool ! I did not know about infinite precision integer being
proposed, that's awsome.

Thanks for the NVI info, but I don't think this is the issue here (I
am terrible at describing the issue here).

Thanks
-Mathieu
 
M

mathieu

Not sure exactly what you are trying to achieve, but this works well
with template classes instead of template functions.
It does use polymorphism, but that is just another way of defining an
interface:

#include <iostream>

template <typename T>
struct Base
{
virtual void foo(T x) = 0;

};

template <typename T>
struct A: Base<T>
{
void foo(T x) { std::cout << "A=" << x << "\n"; }

};

template <typename T>
struct B: Base<T>
{
void foo(T x) { std::cout << "B=" << x << "\n"; }

};

int main(int argc)
{
Base<int> *base;
if (argc > 1)
base = new A<int>;
else
base = new B<int>;
base->foo(45);
return 0;

}


I can't move the template at the class level unfortunately.

Explanation:
My objects can be serialized either in big endian or little endian. So
the design is :

struct Object
{
...
void Read<Swapper>(ostream &os) { ... }
void Write<Swapper>(ostream &os) { ... }
}

Where swapper is either a no-op or actually do a byte-swap. So
template at the class level would not make much sense.

Thanks anyway,
-Mathieu
 
J

James Kanze

On 26 oct, 23:40, (e-mail address removed) wrote:
My objects can be serialized either in big endian or little endian.

Depending on what?
So
the design is :

struct Object
{
...
void Read<Swapper>(ostream &os) { ... }
void Write<Swapper>(ostream &os) { ... }
}
Where swapper is either a no-op or actually do a byte-swap. So
template at the class level would not make much sense.

I'm not sure I understand this at all. Why would you want to
swap anything. You have two output formats. If the choice is
runtime, you have to include the implementation of both in your
code. If the choice is compile time, you include one or the
other at link time. In both cases, it's pretty transparent to
the user; in the first case, you use the strategy pattern, and
the user has to specify the strategy once up front, and in the
second, it's totally transparent, since the decision is made
when you link.

And of course, you never "swap" anything. You simply write the
data in the target format.
 
M

mathieu

Depending on what?



I'm not sure I understand this at all. Why would you want to
swap anything. You have two output formats. If the choice is
runtime, you have to include the implementation of both in your
code. If the choice is compile time, you include one or the
other at link time. In both cases, it's pretty transparent to
the user; in the first case, you use the strategy pattern, and
the user has to specify the strategy once up front, and in the
second, it's totally transparent, since the decision is made
when you link.

And of course, you never "swap" anything. You simply write the
data in the target format.

Hi James,

That is correct I have 'two' implementations (only different by one
operation: byte swapping). Thus my Object O can be written in little
or big endian. Therefore the 'endian' template parameter cannot be at
the class level (as suggested by previous poster).
Since code is cross platform, and choice is done at run-time (user
cannot know ahead of time which format he is reading) I simply pass a
'swapper' template parameter that will decide depending on file format
and architecture how to read words.

Thanks for your interest,
-Mathieu
 
J

James Kanze

That is correct I have 'two' implementations (only different by one
operation: byte swapping).

But why would you ever "swap" bytes? You write big endian, or
you write little endian. Or you write some other format---in
all cases, you have a *value* (int, whatever) which must be
output in a specified format. You convert the value to the
format, and that's it.
Thus my Object O can be written in little or big endian.
Therefore the 'endian' template parameter cannot be at the
class level (as suggested by previous poster).
Since code is cross platform, and choice is done at run-time (user
cannot know ahead of time which format he is reading) I simply pass a
'swapper' template parameter that will decide depending on file format
and architecture how to read words.

If the choice is run-time, you can't use templates. I'd go with
the strategy pattern. Something like:

class Writer
{
public:
virtual ~Writer() {}
virtual void write( std::eek:stream& dest, int value ) const =
0 ;
// and so on for each type.
}

class BEWriter : public Writer
{
public:
virtual void write( std::eek:stream& dest, int value ) const
{
uint32_t tmp = value ;
dest.put( (tmp >> 24) & 0xFF ) ;
dest.put( (tmp >> 16) & 0xFF ) ;
dest.put( (tmp >> 8) & 0xFF ) ;
dest.put( (tmp ) & 0xFF ) ;
}
// and so on for each type...
} ;

class LEWriter : public Writer
{
public:
virtual void write( std::eek:stream& dest, int value ) const
{
uint32_t tmp = value ;
dest.put( (tmp ) & 0xFF ) ;
dest.put( (tmp >> 8) & 0xFF ) ;
dest.put( (tmp >> 16) & 0xFF ) ;
dest.put( (tmp >> 24) & 0xFF ) ;
}
// and so on for each type...
} ;

You then initialize a Writer* in your output class to whichever
one is relevant, and output through it. You can even change on
the fly---first 10 integers in big endian, next 10 in little,
etc.
 
M

mathieu

But why would you ever "swap" bytes? You write big endian, or
you write little endian. Or you write some other format---in
all cases, you have a *value* (int, whatever) which must be
output in a specified format. You convert the value to the
format, and that's it.


If the choice is run-time, you can't use templates. I'd go with
the strategy pattern. Something like:

class Writer
{
public:
virtual ~Writer() {}
virtual void write( std::eek:stream& dest, int value ) const =
0 ;
// and so on for each type.
}

class BEWriter : public Writer
{
public:
virtual void write( std::eek:stream& dest, int value ) const
{
uint32_t tmp = value ;
dest.put( (tmp >> 24) & 0xFF ) ;
dest.put( (tmp >> 16) & 0xFF ) ;
dest.put( (tmp >> 8) & 0xFF ) ;
dest.put( (tmp ) & 0xFF ) ;
}
// and so on for each type...
} ;

class LEWriter : public Writer
{
public:
virtual void write( std::eek:stream& dest, int value ) const
{
uint32_t tmp = value ;
dest.put( (tmp ) & 0xFF ) ;
dest.put( (tmp >> 8) & 0xFF ) ;
dest.put( (tmp >> 16) & 0xFF ) ;
dest.put( (tmp >> 24) & 0xFF ) ;
}
// and so on for each type...
} ;

You then initialize a Writer* in your output class to whichever
one is relevant, and output through it. You can even change on
the fly---first 10 integers in big endian, next 10 in little,
etc.

Hi James,

Thanks for taking the time to detail your solution. However I still
think that template apply in this case. I am basically doing the same,
except that I have a Swapper class that is doing the:

void DoTheIntSwap() {
(tmp ) & 0xFF ;
(tmp >> 8) & 0xFF ;
(tmp >> 16) & 0xFF ;
(tmp >> 24) & 0xFF ;
}

Therefore that I can reuse code in between Reader & Writer:

http://gdcm.svn.sourceforge.net/viewvc/gdcm/trunk/Source/Common/gdcmSwapper.h?view=markup

Finally you use -I think- the visitor pattern. I tried it and it
gives something real ugly:

http://gdcm.svn.sourceforge.net/vie...n/gdcmIOSerialize.h?revision=1297&view=markup

Basically I need to make IOSerialize of friend of every single
structure I am using in my code. Pretty ugly, heh ?
So instead each structure has access to its private member and can
easily write them. Eg.

template <typename TSwap>
IStream &Read(IStream &is)
{
is.read(ElementTag.bytes, 4);
TSwap::SwapArray(ElementTag.tags, 2); // Will expand to nothing
when writing file format match OS (e.g Big Endian file on big endian
machine)
return is;
}

template <typename TSwap>
const OStream &Write(OStream &os) const
{
uint16_t copy[2]; // local copy since function is
const
copy[0]= ElementTag.tags[0];
copy[1]= ElementTag.tags[1];
TSwap::SwapArray(copy, 2);
return os.write((char*)(&copy), 4);
}

Ref:
http://gdcm.svn.sourceforge.net/vie.../DataStructureAndEncodingDefinition/gdcmTag.h

Thanks again for your time, this is very much appreciated as it took
me multiple iterations and I am still not entirely satisfied: template
functions in non-template classes are ugly to use :(

-Mathieu
 

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

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top