Templates HowTo?

K

keith

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?
 
A

Alf P. Steinbach

* (e-mail address 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;
};

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::processFunc const AAAFunctions::process = &AAA_setup;
CFunctionTypes::DoneFunc 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::process( ..., ..., &myContext );
}
};

typedef WorkWrapper<AAAFcuntions> AAAWork;

Or something like that.


Cheers, & hth.,

- Alf
 
K

keith

* (e-mail address removed):


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

Alf, I sincerely appreciate the feedback.

Now I need to sit down in a dark room and think about what you said
until I understand :) And probably look up the keyword 'functor'.
 
A

Alf P. Steinbach

* (e-mail address removed):
Alf, I sincerely appreciate the feedback.

Now I need to sit down in a dark room and think about what you said
until I understand :) And probably look up the keyword 'functor'.

Hm. I can see your reply, but not my posting.

So I had to check in my "Local Folders/Sent".

And discovered typo:

int operator( whatever )

should be the function call operator

int operator()( whatever )

where "whatever" means add in the arguments you need.


Cheers, & sorry for typo,

- Alf
 
J

Joe Greer

(e-mail address removed) wrote in (e-mail address 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?

Sadly, this isn't ideal for templates, because templates works on types
and it seems that what you want is token concatenation. If things are
as you have it above, then it seems that a macro would be more
appropriate to generate the wrapper classes or create a class which
contains some function pointers and initialize that. Once you have your
functions wrapped in a class, then you can use templates to apply
algorithms to those classes.

As a side note, it does seem like both templates and macros are all
about code generation. Templates do it in a completely type safe manner
and macros in a manner that doesn't obey any scoping rules, but is
potentially useful anyway. It would be nice if templates could be
enhanced with some token pasting functionality so that stuff like this
could be safely expressed. Something like

template<token P>
class Work
{
Work() { P##setup(); }
~Work() { P##_done(); }
};

Then it could be instantiated with a token and the code generated. It
seems like it should be doable. Oh well, I could dream I suppose.

For the OP, the above does NOT work. Templates work with types and some
limited PODs but not arbitrary tokens or text.

joe
 
K

keith

* (e-mail address removed):

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. Here's what I've got so far with Alf's help:

//--------------------- ltc.h START------------------------
#include "tomcrypt.h"

/* Our abstract base class */
class AbstractCipher
{
public:
virtual ~AbstractCipher() {}
virtual int setup (const uint8_t* key, int keylen) = 0;
virtual int ecb_encrypt (const uint8_t *ptext, uint8_t* ctext) = 0;
virtual int ecb_decrypt (const uint8_t *ctext, uint8_t* ptext) = 0;
virtual int test () = 0;
virtual int keysize (int* keysize) = 0;
virtual void done () = 0;
virtual int index () = 0;
};

/* Our functors */
struct CFunctionTypes
{
typedef int (*Setup)
(const uint_8t* key, int keylen, int rounds, symmetric_key* skey);
typedef int (*ECBencrypt)
(const uint8_t *ptext, uint8_t* ctext, symmetric_key* skey);
typedef int (*ECBdecrypt)
(const uint8_t *ctext, uint8_t* ptext, symmetric_key* skey);
typedef int (*Test)
();
typedef int (*Keysize)
(int* keysize);
typedef void (*Done)
(symmetric_key* skey);
};

/* Now specialise the functors for each cipher */
struct AESCipher:CFunctionTypes
{
static Setup const setup;
static ECBencrypt const ecb_encrypt;
static ECBdecrypt const ecb_decrypt;
static Test const test;
static Keysize const keysize;
static Done const done;
AESCipher() { register_cipher(&aes_desc); index =
find_cipher("aes");}
~AESCipher() { unregister_cipher(&aes_desc); }

int index;
};

CFunctionTypes::Setup const AESCipher::setup = &aes_setup;
CFunctionTypes::ECBencrypt const AESCipher::ecb_encrypt =
&aes_ecb_encrypt;
CFunctionTypes::ECBdecrypt const AESCipher::ecb_decrypt =
&aes_ecb_decrypt;
CFunctionTypes::Test const AESCipher::test = &aes_test;
CFunctionTypes::Keysize const AESCipher::keysize =
&aes_keysize;
CFunctionTypes::Done const AESCipher::done = &aes_done;

struct TwofishCipher:CFunctionTypes
{
static Setup const setup;
static ECBencrypt const ecb_encrypt;
static ECBdecrypt const ecb_decrypt;
static Test const test;
static Keysize const keysize;
static Done const done;
TwofishCipher() { register_cipher(&twofish_desc); index =
find_cipher("twofish");}
~TwofishCipher() { unregister_cipher(&twofish_desc); }

int index;
};

CFunctionTypes::Setup const TwofishCipher::setup =
&twofish_setup;
CFunctionTypes::ECBencrypt const TwofishCipher::ecb_encrypt =
&twofish_ecb_encrypt;
CFunctionTypes::ECBdecrypt const TwofishCipher::ecb_decrypt =
&twofish_ecb_decrypt;
CFunctionTypes::Test const TwofishCipher::test =
&twofish_test;
CFunctionTypes::Keysize const TwofishCipher::keysize =
&twofish_keysize;
CFunctionTypes::Done const TwofishCipher::done =
&twofish_done;

template < class CFunctions > class Cipher : public AbstractCipher
{
public:
Cipher () : skey() {}

virtual int setup (const uint_8t* key, int keylen)
{ return CFunctions::setup (key, keylen, 0, &skey); }

virtual int ecb_encrypt (const uint8_t *ptext, uint8_t* ctext)
{ return CFunctions::ecb_encrypt (ptext, ctext, &skey); }

virtual int ecb_decrypt (const uint8_t *ctext, uint8_t* ptext)
{ return CFunctions::ecb_decrypt (ctext, ptext, &skey); }

virtual int test ()
{ return CFunctions::test(); }

virtual int keysize (int* keysize)
{ return CFunctions::keysize(keysize); }

virtual int index()
{ return CFunctions::index; }

virtual ~Cipher ()
{ CFunctions::done (&skey); }

protected:
symmetric_key skey;
};

typedef Cipher < AESCipher > AES;
typedef Cipher < TwofishCipher > Twofish;

//--------------------- ltc.h END------------------------
//--------------------- x.cc BEGIN-----------------------
#include <iostream>
#include "ltc.h"

int main()
{
AES aes;
std::cerr << "AES.index() = " << aes.index();
return 0;
}

//--------------------- x.cc END-------------------------

Now you can probably see what I'm trying to do with this 'index' call;
it has to return the result of find_cipher("ciphername"). The problem
is that at compile time, I get the following error:

ltc.h: In member function 'int Cipher<CFunctions>::index() [with
CFunctions = AESCipher]':
x.cc:47: instantiated from here
ltc.h:40: error: object missing in reference to 'AESCipher::index'
ltc.h:93: error: from this location

I can't see what it is that's missing. If someone can clarify, or
even better, show me a better way to do what I'm trying to do, I'd be
grateful. Meanwhile, I'm learning a _lot_ doing this stuff!

k

1. http://libtom.org/
 
A

Alf P. Steinbach

* (e-mail address removed):
* (e-mail address removed):
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. Here's what I've got so far with Alf's help:

//--------------------- ltc.h START------------------------
#include "tomcrypt.h"

/* Our abstract base class */
class AbstractCipher
{
public:
virtual ~AbstractCipher() {}
virtual int setup (const uint8_t* key, int keylen) = 0;
virtual int ecb_encrypt (const uint8_t *ptext, uint8_t* ctext) = 0;
virtual int ecb_decrypt (const uint8_t *ctext, uint8_t* ptext) = 0;
virtual int test () = 0;
virtual int keysize (int* keysize) = 0;
virtual void done () = 0;
virtual int index () = 0;
};

/* Our functors */
struct CFunctionTypes
{
typedef int (*Setup)
(const uint_8t* key, int keylen, int rounds, symmetric_key* skey);
typedef int (*ECBencrypt)
(const uint8_t *ptext, uint8_t* ctext, symmetric_key* skey);
typedef int (*ECBdecrypt)
(const uint8_t *ctext, uint8_t* ptext, symmetric_key* skey);
typedef int (*Test)
();
typedef int (*Keysize)
(int* keysize);
typedef void (*Done)
(symmetric_key* skey);
};

/* Now specialise the functors for each cipher */
struct AESCipher:CFunctionTypes
{
static Setup const setup;
static ECBencrypt const ecb_encrypt;
static ECBdecrypt const ecb_decrypt;
static Test const test;
static Keysize const keysize;
static Done const done;
AESCipher() { register_cipher(&aes_desc); index =
find_cipher("aes");}
~AESCipher() { unregister_cipher(&aes_desc); }

int index;
};

CFunctionTypes::Setup const AESCipher::setup = &aes_setup;
CFunctionTypes::ECBencrypt const AESCipher::ecb_encrypt =
&aes_ecb_encrypt;
CFunctionTypes::ECBdecrypt const AESCipher::ecb_decrypt =
&aes_ecb_decrypt;
CFunctionTypes::Test const AESCipher::test = &aes_test;
CFunctionTypes::Keysize const AESCipher::keysize =
&aes_keysize;
CFunctionTypes::Done const AESCipher::done = &aes_done;

struct TwofishCipher:CFunctionTypes
{
static Setup const setup;
static ECBencrypt const ecb_encrypt;
static ECBdecrypt const ecb_decrypt;
static Test const test;
static Keysize const keysize;
static Done const done;
TwofishCipher() { register_cipher(&twofish_desc); index =
find_cipher("twofish");}
~TwofishCipher() { unregister_cipher(&twofish_desc); }

int index;
};

CFunctionTypes::Setup const TwofishCipher::setup =
&twofish_setup;
CFunctionTypes::ECBencrypt const TwofishCipher::ecb_encrypt =
&twofish_ecb_encrypt;
CFunctionTypes::ECBdecrypt const TwofishCipher::ecb_decrypt =
&twofish_ecb_decrypt;
CFunctionTypes::Test const TwofishCipher::test =
&twofish_test;
CFunctionTypes::Keysize const TwofishCipher::keysize =
&twofish_keysize;
CFunctionTypes::Done const TwofishCipher::done =
&twofish_done;

template < class CFunctions > class Cipher : public AbstractCipher
{
public:
Cipher () : skey() {}

virtual int setup (const uint_8t* key, int keylen)
{ return CFunctions::setup (key, keylen, 0, &skey); }

virtual int ecb_encrypt (const uint8_t *ptext, uint8_t* ctext)
{ return CFunctions::ecb_encrypt (ptext, ctext, &skey); }

virtual int ecb_decrypt (const uint8_t *ctext, uint8_t* ptext)
{ return CFunctions::ecb_decrypt (ctext, ptext, &skey); }

virtual int test ()
{ return CFunctions::test(); }

virtual int keysize (int* keysize)
{ return CFunctions::keysize(keysize); }

virtual int index()
{ return CFunctions::index; }

virtual ~Cipher ()
{ CFunctions::done (&skey); }

protected:
symmetric_key skey;
};

typedef Cipher < AESCipher > AES;
typedef Cipher < TwofishCipher > Twofish;

//--------------------- ltc.h END------------------------
//--------------------- x.cc BEGIN-----------------------
#include <iostream>
#include "ltc.h"

int main()
{
AES aes;
std::cerr << "AES.index() = " << aes.index();
return 0;
}

//--------------------- x.cc END-------------------------

Now you can probably see what I'm trying to do with this 'index' call;
it has to return the result of find_cipher("ciphername"). The problem
is that at compile time, I get the following error:

ltc.h: In member function 'int Cipher<CFunctions>::index() [with
CFunctions = AESCipher]':
x.cc:47: instantiated from here
ltc.h:40: error: object missing in reference to 'AESCipher::index'
ltc.h:93: error: from this location

I can't see what it is that's missing. If someone can clarify, or
even better, show me a better way to do what I'm trying to do, I'd be
grateful. Meanwhile, I'm learning a _lot_ doing this stuff!

Well, AESCipher::index is non-static data member, that is, it only
exists in instances of AESCipher, one per instance.

To access it you'd need an AESCipher instance, but AESCipher is
currently not designed for instantiation: it's only designed as a
compile time carrier of addresses (by the way, I forgot, those function
types should probably all be 'extern "C"', but that's just a nit).

What you need seems to be singletons, and for singletons the usual way,
when you don't have other requirements, is the "Meyer's singleton"

class SingletonType
{
private:
SingletonType( SingletonType const& );
SingletonType& operator=( SingletonType const& );

public:
static SingletonType& instance()
{
static SingletonType theInstance; // Add constructor args
return theInstance;
}
};

It might also be a good idea to put all initialization calls in
constructors, and all cleanup calls in destructors. That is, no "setup"
or "cleanup" functions visible to client code at the C++ level. Of
course there may be some requirement I don't know or understand, but
usually, constructors and destructors are best for initialization and
cleanup: it automates the calls, and makes for better exception safety.


Cheers, & hth.,

- Alf
 
K

keith

* (e-mail address removed):
Now you can probably see what I'm trying to do with this 'index' call;
it has to return the result of find_cipher("ciphername"). The problem
is that at compile time, I get the following error:
ltc.h: In member function 'int Cipher<CFunctions>::index() [with
CFunctions = AESCipher]':
x.cc:47: instantiated from here
ltc.h:40: error: object missing in reference to 'AESCipher::index'
ltc.h:93: error: from this location
I can't see what it is that's missing. If someone can clarify, or
even better, show me a better way to do what I'm trying to do, I'd be
grateful. Meanwhile, I'm learning a _lot_ doing this stuff!

Well, AESCipher::index is non-static data member, that is, it only
exists in instances of AESCipher, one per instance.

To access it you'd need an AESCipher instance, but AESCipher is
currently not designed for instantiation: it's only designed as a
compile time carrier of addresses (by the way, I forgot, those function
types should probably all be 'extern "C"', but that's just a nit).

OK, I understand that.
What you need seems to be singletons, and for singletons the usual way,
when you don't have other requirements, is the "Meyer's singleton"

class SingletonType
{
private:
SingletonType( SingletonType const& );
SingletonType& operator=( SingletonType const& );

public:
static SingletonType& instance()
{
static SingletonType theInstance; // Add constructor args
return theInstance;
}
};

Sorry, I don't see how to apply this to what I'm doing. I think it's
getting time for more coffee... Can you expand a little for me?
It might also be a good idea to put all initialization calls in
constructors, and all cleanup calls in destructors. That is, no "setup"
or "cleanup" functions visible to client code at the C++ level. Of
course there may be some requirement I don't know or understand, but
usually, constructors and destructors are best for initialization and
cleanup: it automates the calls, and makes for better exception safety.

All of that is fair, and once I have the thing doing what I want, I
will probably move that stuff out of sight.

Thanks again for the help so far.
 
K

keith

S'ok - I cracked it!

Here's the final, working wrapper (for now). All I had to do was put
an extra static member into each specialisation, containing the name
of the cipher. Obvious after a good night's sleep! Now somebody will
tell me at least one very good reason why I shouldn't do that :)

//=---------------------BEGIN--------------------
#include "tomcrypt.h"

/* Our abstract base class */
class AbstractCipher
{
public:
virtual ~AbstractCipher() {}
virtual int setup (const uint8_t* key, int keylen) = 0;
virtual int ecb_encrypt (const uint8_t *ptext, uint8_t* ctext) = 0;
virtual int ecb_decrypt (const uint8_t *ctext, uint8_t* ptext) = 0;
virtual int test () = 0;
virtual int keysize (int* keysize) = 0;
virtual int index () = 0;
};

/* Our functors */
struct CFunctionTypes
{
typedef int (*Setup)
(const uint8_t* key, int keylen, int rounds, symmetric_key* skey);
typedef int (*ECBencrypt)
(const uint8_t *ptext, uint8_t* ctext, symmetric_key* skey);
typedef int (*ECBdecrypt)
(const uint8_t *ctext, uint8_t* ptext, symmetric_key* skey);
typedef int (*Test)
();
typedef int (*Keysize)
(int* keysize);
typedef void (*Done)
(symmetric_key* skey);

typedef const struct ltc_cipher_descriptor* Desc;
typedef const char* Name;
};

/* Now specialise the functors for each cipher */
struct AESCipher:CFunctionTypes
{
static Setup const setup;
static ECBencrypt const ecb_encrypt;
static ECBdecrypt const ecb_decrypt;
static Test const test;
static Keysize const keysize;
static Done const done;
static Desc const descriptor;
static Name const name;
};

CFunctionTypes::Setup const AESCipher::setup = &aes_setup;
CFunctionTypes::ECBencrypt const AESCipher::ecb_encrypt =
&aes_ecb_encrypt;
CFunctionTypes::ECBdecrypt const AESCipher::ecb_decrypt =
&aes_ecb_decrypt;
CFunctionTypes::Test const AESCipher::test = &aes_test;
CFunctionTypes::Keysize const AESCipher::keysize =
&aes_keysize;
CFunctionTypes::Done const AESCipher::done = &aes_done;
CFunctionTypes::Desc const AESCipher::descriptor = &aes_desc;
CFunctionTypes::Name const AESCipher::name = "aes";

struct TwofishCipher:CFunctionTypes
{
static Setup const setup;
static ECBencrypt const ecb_encrypt;
static ECBdecrypt const ecb_decrypt;
static Test const test;
static Keysize const keysize;
static Done const done;
static Desc const descriptor;
static Name const name;
};

CFunctionTypes::Setup const TwofishCipher::setup =
&twofish_setup;
CFunctionTypes::ECBencrypt const TwofishCipher::ecb_encrypt =
&twofish_ecb_encrypt;
CFunctionTypes::ECBdecrypt const TwofishCipher::ecb_decrypt =
&twofish_ecb_decrypt;
CFunctionTypes::Test const TwofishCipher::test =
&twofish_test;
CFunctionTypes::Keysize const TwofishCipher::keysize =
&twofish_keysize;
CFunctionTypes::Done const TwofishCipher::done =
&twofish_done;
CFunctionTypes::Desc const TwofishCipher::descriptor =
&twofish_desc;
CFunctionTypes::Name const TwofishCipher::name =
"twofish";

template < class CFunctions > class Cipher : public AbstractCipher
{
public:
Cipher () : skey()
{ register_cipher (CFunctions::descriptor); }

Cipher (const uint8_t* key, int keylen) : skey()
{ register_cipher (CFunctions::descriptor);
CFunctions::setup (key, keylen, 0, &skey);
}

virtual int setup (const uint8_t* key, int keylen)
{ return CFunctions::setup (key, keylen, 0, &skey); }

virtual int ecb_encrypt (const uint8_t *ptext, uint8_t* ctext)
{ return CFunctions::ecb_encrypt (ptext, ctext, &skey); }

virtual int ecb_decrypt (const uint8_t *ctext, uint8_t* ptext)
{ return CFunctions::ecb_decrypt (ctext, ptext, &skey); }

virtual int test ()
{ return CFunctions::test(); }

virtual int keysize (int* keysize)
{ return CFunctions::keysize(keysize); }

virtual int index ()
{ return find_cipher(CFunctions::name); }

virtual ~Cipher ()
{ CFunctions::done (&skey);
unregister_cipher (CFunctions::descriptor);
}

protected:
symmetric_key skey;
};

typedef Cipher < AESCipher > AES;
typedef Cipher < TwofishCipher > Twofish;

//=---------------------END--------------------
 
J

James Kanze

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.

It's not templates you need, but macros. Something like:

class AbstractWork : private context
{
public:
virtual ~AbstractWork() {}
virtual int setup( uint8_t const* p1, int p2 ) = 0 ;
virtual int process( uint8_t const* p1, uint8_t* p2 )
= 0 ;
virtual int done() = 0 ;
} ;

#define Whatever( prefix )
class Work ## prefix : public AbstractWork
{
public:
virtual int setup( uint8_t const* p1, int p2 )
{
return prefix ## _setup( p1, p2, this ) ;
}
virtual int _process( uint8_t const* p1, uint8_t* p2 )
{
return prefix ## process( p1, p2, this ) ;
}
virtual int _done()
{
return prefix ## done( this ) ;
}
}

Whatever( AAA ) ;
Whatever( BBB ) ;
Whatever( CCC ) ;

The abstract base class may or may not be necessary at the
interface level---in this case, it's also handy for maintaining
the context structure; if you provide the necessary factory
functions in the file which defines the concrete classes, it
will also prevent the macro name from poluting the client code.
(E.g. put the definition of the abstract class in a header file,
along with the declarations of the factory functions, and just
define the macro in an implementation file which invokes it.)
 
J

James Kanze

* (e-mail address removed):
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::processFunc const AAAFunctions::process = &AAA_setup;
CFunctionTypes::DoneFunc 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::process( ..., ..., &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).
 
A

Alf P. Steinbach

* James Kanze:
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

Alf P. Steinbach

* Alf P. Steinbach:
* James Kanze:

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



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
 
J

James Kanze

Each of group AAA, BBB and CCC essentially constitute a functor object.
[/QUOTE]
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::eek: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).
 
J

James Kanze

* James Kanze:
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.
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).
 

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,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top