Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Templates HowTo?

Reply
Thread Tools

Templates HowTo?

 
 
James Kanze
Guest
Posts: n/a
 
      02-22-2008
On Feb 21, 10:20 am, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
> * (E-Mail Removed):
> > I must confess I'm completely new to templates, having
> > mainly used C++ for quite a while as 'C with Classes', but
> > now I have an application which seems to cry out for
> > templates, but I just can't get my head around how to use
> > them in this situation. Any assistance or pointers to other
> > resources would be welcomed.


> > Let's say I have a (third party, unmodifiable) library of C
> > functions which provide the following interfaces:


> > int AAA_setup(const uint8_t* p1, int p2, struct context* p3);
> > int AAA_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
> > int AAA_done(struct context* p1);


> > int BBB_setup(const uint8_t* p1, int p2, struct context* p3);
> > int BBB_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
> > int BBB_done(struct context* p1);


> > int CCC_setup(const uint8_t* p1, int p2, struct context* p3);
> > int CCC_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
> > int CCC_done(struct context* p1);


> > What I want to do is provide a class wrapper for all this so that in
> > my C++ application I can do something like:


> > Work<BBB> thing;
> > thing.setup(&data1, val);
> > thing.process(&in, &out);
> > thing.done();


> > (The pointer to the context structure can obviously disappear as
> > private data within the class)


> > Now this seems from my reading to be the sort of thing that templates
> > are probably good at, but I can't for the life of me see how to do it,
> > without first creating a separate class for each of AAA, BBB, and CCC.


> > Can anyone enlighten me please?


> Each of group AAA, BBB and CCC essentially constitute a functor object.


> class AbstractWork
> {
> public:
> virtual ~AbstractWork() {}
> virtual int operator( whatever ) = 0;
> };


You've lost me there. Each group proposes three functions, not
one. (And you never use AbstractWork later, either. Is it
maybe part of an idea you had to start with, that didn't pan
out, and you forgot to delete it?)

> struct CFunctionTypes
> {
> typedef (*SetupFunc)
> (const uint8_t* p1, int p2, struct context* p3);


> typedef (*ProcessFunc)
> (const uint8_t* p1, uint8_t* p2, struct context* p3);


> typedef (*DoneFunc)
> (struct context* p1);
> };


> struct AAAFunctions: CFunctionTypes
> {
> static SetupFunc const setup;
> static ProcessFunc const process;
> static DoneFunc const done;
> };


> CFunctionTypes::SetupFunc const AAAFunctions::setup = &AAA_setup;
> CFunctionTypes:rocessFunc const AAAFunctions:rocess = &AAA_setup;
> CFunctionTypes:oneFunc const AAAFunctions::done = &AAA_done;


> template< class CFunctions >
> class WorkWrapper
> {
> protected:
> context myContext;


> public:
> WorkWrapper( uint8_t a, int b )
> {
> if( !CFunctions::setup( &a, b, &myContext ) )
> { throwX( "urk" ); }
> }


> virtual ~WorkWrapper() { CFunctions::done( &myContext ); }


> virtual int operator( whatever )
> {
> return CFunctions:rocess( ..., ..., &myContext );
> }
> };


> typedef WorkWrapper<AAAFcuntions> AAAWork;


That's actually pretty clever. I'll admit that as soon as I saw
the C style naming convention, macros and token pasting came to
mind---I probably used <generic.h> too much in the old days.

A pure macro solution probably takes less typing, but if I were
to use macros, I'd definitly use an abstract base class and
factory functions in order to avoid having to define a macro in
the client header (and thus poluting the clients namespace).
Which, of course, results in more lines of code (but might have
other advantages as well, so that you'd want to use it anyway).

--
James Kanze (GABI Software) email:(E-Mail Removed)
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
 
Reply With Quote
 
 
 
 
Alf P. Steinbach
Guest
Posts: n/a
 
      02-22-2008
* James Kanze:
> On Feb 21, 10:20 am, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
>> * (E-Mail Removed):
>>> I must confess I'm completely new to templates, having
>>> mainly used C++ for quite a while as 'C with Classes', but
>>> now I have an application which seems to cry out for
>>> templates, but I just can't get my head around how to use
>>> them in this situation. Any assistance or pointers to other
>>> resources would be welcomed.

>
>>> Let's say I have a (third party, unmodifiable) library of C
>>> functions which provide the following interfaces:

>
>>> int AAA_setup(const uint8_t* p1, int p2, struct context* p3);
>>> int AAA_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
>>> int AAA_done(struct context* p1);

>
>>> int BBB_setup(const uint8_t* p1, int p2, struct context* p3);
>>> int BBB_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
>>> int BBB_done(struct context* p1);

>
>>> int CCC_setup(const uint8_t* p1, int p2, struct context* p3);
>>> int CCC_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
>>> int CCC_done(struct context* p1);

>
>>> What I want to do is provide a class wrapper for all this so that in
>>> my C++ application I can do something like:

>
>>> Work<BBB> thing;
>>> thing.setup(&data1, val);
>>> thing.process(&in, &out);
>>> thing.done();

>
>>> (The pointer to the context structure can obviously disappear as
>>> private data within the class)

>
>>> Now this seems from my reading to be the sort of thing that templates
>>> are probably good at, but I can't for the life of me see how to do it,
>>> without first creating a separate class for each of AAA, BBB, and CCC.

>
>>> Can anyone enlighten me please?

>
>> Each of group AAA, BBB and CCC essentially constitute a functor object.

>
>> class AbstractWork
>> {
>> public:
>> virtual ~AbstractWork() {}
>> virtual int operator( whatever ) = 0;
>> };

>
> You've lost me there. Each group proposes three functions, not
> one.


Each group essentially consists of initialization, do-it, and
destructor, and that's a functor.


> (And you never use AbstractWork later, either. Is it
> maybe part of an idea you had to start with, that didn't pan
> out, and you forgot to delete it?)


It's a means to be able to treat the functors polymorphically.


Cheers, & hth.,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
 
Reply With Quote
 
 
 
 
Alf P. Steinbach
Guest
Posts: n/a
 
      02-22-2008
* Alf P. Steinbach:
> * James Kanze:
>> On Feb 21, 10:20 am, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
>>> * (E-Mail Removed):
>>>> I must confess I'm completely new to templates, having
>>>> mainly used C++ for quite a while as 'C with Classes', but
>>>> now I have an application which seems to cry out for
>>>> templates, but I just can't get my head around how to use
>>>> them in this situation. Any assistance or pointers to other
>>>> resources would be welcomed.

>>
>>>> Let's say I have a (third party, unmodifiable) library of C
>>>> functions which provide the following interfaces:

>>
>>>> int AAA_setup(const uint8_t* p1, int p2, struct context* p3);
>>>> int AAA_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
>>>> int AAA_done(struct context* p1);

>>
>>>> int BBB_setup(const uint8_t* p1, int p2, struct context* p3);
>>>> int BBB_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
>>>> int BBB_done(struct context* p1);

>>
>>>> int CCC_setup(const uint8_t* p1, int p2, struct context* p3);
>>>> int CCC_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
>>>> int CCC_done(struct context* p1);

>>
>>>> What I want to do is provide a class wrapper for all this so that in
>>>> my C++ application I can do something like:

>>
>>>> Work<BBB> thing;
>>>> thing.setup(&data1, val);
>>>> thing.process(&in, &out);
>>>> thing.done();

>>
>>>> (The pointer to the context structure can obviously disappear as
>>>> private data within the class)

>>
>>>> Now this seems from my reading to be the sort of thing that templates
>>>> are probably good at, but I can't for the life of me see how to do it,
>>>> without first creating a separate class for each of AAA, BBB, and CCC.

>>
>>>> Can anyone enlighten me please?

>>
>>> Each of group AAA, BBB and CCC essentially constitute a functor object.

>>
>>> class AbstractWork
>>> {
>>> public:
>>> virtual ~AbstractWork() {}
>>> virtual int operator( whatever ) = 0;
>>> };

>>
>> You've lost me there. Each group proposes three functions, not
>> one.

>
> Each group essentially consists of initialization, do-it, and
> destructor, and that's a functor.
>
>
>> (And you never use AbstractWork later, either. Is it
>> maybe part of an idea you had to start with, that didn't pan
>> out, and you forgot to delete it?)

>
> It's a means to be able to treat the functors polymorphically.


Oh, I see. Da keybrd divel, igain. The operotr should of course be
operator(), as noted elsewhere (hah, I saw that!), and the later
WorkWrapper class should derive from AbstractWork (obisouskly).

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      02-22-2008
On Feb 21, 3:45 pm, (E-Mail Removed) wrote:
> On 21 Feb, 09:20, "Alf P. Steinbach" <(E-Mail Removed)> wrote:


> > * (E-Mail Removed):
> > > Now this seems from my reading to be the sort of thing
> > > that templates are probably good at, but I can't for the
> > > life of me see how to do it, without first creating a
> > > separate class for each of AAA, BBB, and CCC.


> > Each of group AAA, BBB and CCC essentially constitute a functor object.


> Hi again. I'm going to try my luck one more time and ask for
> some more advice on this!


> Alf's suggestions upthread all worked swimmingly, but I'm left
> with one thing that I can't quite work out.


> The actual template is intended as a wrapper for the ciphers
> in LibTomCrypt[1], and one of the things I need to do is find
> out the 'index' of a particular cipher by passing the cipher
> name as a string to a helper function.


That's usually done by maintaining a map of pointers to factory
functions (or factory functors). I use two different solutions,
depending on how dynamic it has to be:

The classical OO solution:

I use functors as the factories here, with pointers to
an abstract base class in the map. Something like:

// in Abstract.hh...
class AbstractType
{
public:
// ...
class Factory
{
public:
virtual ~Factory() {}
virtual AbstractType*
create() const = 0 ;
protected:
explicit Factory( std::string const&
key ) ;
} ;

static AbstractType*newInstance( std::string const&
key ) ;
private:
typedef std::map< std::string, Factory const* >
FactoryMap ;
static FactoryMap& getMap() ;
} ;

// in Abstract.cc...
Factory::Factory(
std::string const& key )
{
FactoryMap& map( getMap() ) ;
assert( map.find( key ) == map.end() ) ;
map.insert( FactoryMap::value_type( key, this ) ) ;
}

AbstractType*
AbstractType::newInstance(
std::string const& key )
{
FactoryMap const& map( getMap() ) ;
FactoryMap::const_iterator
factory( map.find( key ) ) ;
return factory == map.end()
? NULL
: factory->second->create() ;
}

FactoryMap&
Factory::getMap()
{
static FactoryMap* theOneAndOnly = NULL ;
if ( theOneAndOnly == NULL ) {
theOneAndOnly = new FactoryMap ;
}
return *theOneAndOnly ;
}

// in Concrete.hh
class ConcreteType : public AbstractType
{
// typically, *nothing* public !
private:
class ConcreteFactory ;
static ConcreteFactory
ourFactory ;
} ;

// in Concrete.cc
class ConcreteType::ConcreteFactory : public
AbstractType::Factory
{
public:
ConcreteFactory() ;
virtual AbstractType*
create() const ;
}

ConcreteType::ConcreteFactory
ConcreteType:urFactory ;

ConcreteType::ConcreteFactory::ConcreteFactory()
: AbstractType::Factory( "Concrete" )
{
}

AbstractType*
ConcreteType::ConcreteFactory::create() const
{
return new ConcreteType ;
}

(Note that in this implementation, all of the
ConcreteFactory's must be registered before threading
starts. If this is not the case, then you need to grab a
lock before the if in getMap(), and getMap() should return a
boost::shared_ptr which releases it in the "destructor".)

In your particular case, you probably want to use
std::auto_ptr for the return types of create() and
newInstance(), since your abstract type is a polymorphic
agent, and not an entity object, which means that the client
is responsible for deleting it---it doesn't manage its own
lifetime. (For that matter, you might even use
std::auto_ptr for an entity object, to manage it until it
has enrolled for whatever events are relevant to its
lifetime.)

In order to add a concrete type to your application, just
link in Concrete. Dynamically, even. Say months after the
application has been running. (I use this technique a lot
in servers---you can add features without stopping the
server.)

The simple solution if you know "up front" all of the types that
will exist.

// in Abstract.hh...
class AbstractType
{
public:
// ...
static AbstractType*newInstance( std::string const&
key ) ;
} ;

// in Abstract.cc
namespace {

template< typename T >
struct StaticMap
{
char const* key ;
T value ;
class Match
{
public:
explicit Match( std::string const& target )
: myTarget( target )
{
}
bool operator()( StaticMap const* obj )
const
{
return myTarget == key ;
}
private:
std::string myTarget ;
} ;
} ;

typedef StaticMap< AbstractType* (*)() >
FactoryMap ;

template< typename Concrete >
AbstractType*
concreteFactory()
{
return Concrete ;
}

FactoryMap const map[] =
{
{ "AAA", &concreteFactory< AAA > },
{ "BBB", &concreteFactory< BBB > },
// ...
}
}

AbstractType*
AbstractType::newInstance(
std::string const& key )
{
FactoryMap const* factory
= std::find_if( begin( map ), end( map ),
FactoryMap::Match( key ) ) ;
return factory == end( map )
? NULL
: (*factory->factory)() ;
}

This has the advantage of simplicity, and since all static
data is statically initialized (notice the use of char
const* instead of std::string), there are no order of
initialization issues (nor threading issues, since all data
is const as well). It has the disadvantage of requiring all
of the concrete classes to be known in one place. This can
be partially attenuated by moving the actual map out to a
separate file, which is generated automatically by the make
file (e.g. from the list of source files of the concrete
classes).

I use this idiom often enough that StaticMap is part of my
standard library (along with begin() and end(), of course).

--
James Kanze (GABI Software) email:(E-Mail Removed)
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      02-22-2008
On Feb 22, 10:18 am, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
> * James Kanze:
> > On Feb 21, 10:20 am, "Alf P. Steinbach" <(E-Mail Removed)> wrote:
> >> * (E-Mail Removed):
> >>> I must confess I'm completely new to templates, having
> >>> mainly used C++ for quite a while as 'C with Classes', but
> >>> now I have an application which seems to cry out for
> >>> templates, but I just can't get my head around how to use
> >>> them in this situation. Any assistance or pointers to other
> >>> resources would be welcomed.


> >>> Let's say I have a (third party, unmodifiable) library of C
> >>> functions which provide the following interfaces:


> >>> int AAA_setup(const uint8_t* p1, int p2, struct context* p3);
> >>> int AAA_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
> >>> int AAA_done(struct context* p1);


> >>> int BBB_setup(const uint8_t* p1, int p2, struct context* p3);
> >>> int BBB_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
> >>> int BBB_done(struct context* p1);


> >>> int CCC_setup(const uint8_t* p1, int p2, struct context* p3);
> >>> int CCC_process(const uint8_t* p1, uint8_t* p2, struct context* p3);
> >>> int CCC_done(struct context* p1);


> >>> What I want to do is provide a class wrapper for all this
> >>> so that in my C++ application I can do something like:


> >>> Work<BBB> thing;
> >>> thing.setup(&data1, val);
> >>> thing.process(&in, &out);
> >>> thing.done();


> >>> (The pointer to the context structure can obviously disappear as
> >>> private data within the class)


> >>> Now this seems from my reading to be the sort of thing
> >>> that templates are probably good at, but I can't for the
> >>> life of me see how to do it, without first creating a
> >>> separate class for each of AAA, BBB, and CCC.


> >>> Can anyone enlighten me please?


> >> Each of group AAA, BBB and CCC essentially constitute a functor object.


> >> class AbstractWork
> >> {
> >> public:
> >> virtual ~AbstractWork() {}
> >> virtual int operator( whatever ) = 0;
> >> };


> > You've lost me there. Each group proposes three functions, not
> > one.


> Each group essentially consists of initialization, do-it, and
> destructor, and that's a functor.


Aha. I didn't actually look at the names of the functions; I
just saw three functions. I tend to think of functors as not
having much state, but I guess there are exceptions. And of
course, if you have a pointer to the base class, you have to
dereference it to call the function, e.g. (*p)( whatever ). In
such cases, I tend to use named functions, but again, it would
probably depend on the case. If you can't find a name better
than process(), then operator()() is probably the way to go.
And you're certainly right that a function called setup()
belongs in a constructor, and a function called done() in a
destructor.

> > (And you never use AbstractWork later, either. Is it
> > maybe part of an idea you had to start with, that didn't pan
> > out, and you forgot to delete it?)


> It's a means to be able to treat the functors polymorphically.


I think that's what confused me the most. You never derive from
it. (Or use it in any other way.) And I failed to make the
connection with the templated functor (which you obviously meant
to derive, since you just said you wanted to treat it
polymorphically).

--
James Kanze (GABI Software) email:(E-Mail Removed)
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
how to Specializations of function Templates or Overloading Function templates with Templates ? recover C++ 2 07-25-2006 02:55 AM
Monster Templates - Question about Submitting Templates Fred HTML 1 09-26-2005 01:09 AM
Templates within templates Tom McCallum C++ 2 08-04-2004 04:44 PM
Templates templates templates JKop C++ 3 07-21-2004 11:44 AM
using templates in templates John Harrison C++ 8 07-31-2003 12:00 PM



Advertisments