Templates HowTo?

Discussion in 'C++' started by keith@bytebrothers.co.uk, Feb 21, 2008.

  1. Guest

    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?
     
    , Feb 21, 2008
    #1
    1. Advertising

  2. * :
    > 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


    --
    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?
     
    Alf P. Steinbach, Feb 21, 2008
    #2
    1. Advertising

  3. Guest

    On 21 Feb, 09:20, "Alf P. Steinbach" <> wrote:
    > * :
    > > 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.


    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'.
     
    , Feb 21, 2008
    #3
  4. * :
    > On 21 Feb, 09:20, "Alf P. Steinbach" <> wrote:
    >> * :
    >>> 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.

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

    --
    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?
     
    Alf P. Steinbach, Feb 21, 2008
    #4
  5. Joe Greer Guest

    wrote in news:302ac42c-9f52-428e-ae12-
    :

    >
    > 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
     
    Joe Greer, Feb 21, 2008
    #5
  6. Guest

    On 21 Feb, 09:20, "Alf P. Steinbach" <> wrote:
    > * :
    > > 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. 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/
     
    , Feb 21, 2008
    #6
  7. * :
    > On 21 Feb, 09:20, "Alf P. Steinbach" <> wrote:
    >> * :
    >>> 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. 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

    --
    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?
     
    Alf P. Steinbach, Feb 21, 2008
    #7
  8. Guest

    On 21 Feb, 15:15, "Alf P. Steinbach" <> wrote:
    > * :
    ><snip code>
    >
    > > 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.
     
    , Feb 21, 2008
    #8
  9. Guest

    On 21 Feb, 15:25, wrote:
    >
    > > > 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:


    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--------------------
     
    , Feb 22, 2008
    #9
  10. James Kanze Guest

    On Feb 21, 9:42 am, wrote:
    > 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.)

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Feb 22, 2008
    #10
  11. James Kanze Guest

    On Feb 21, 10:20 am, "Alf P. Steinbach" <> wrote:
    > * :
    > > 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::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).

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Feb 22, 2008
    #11
  12. * James Kanze:
    > On Feb 21, 10:20 am, "Alf P. Steinbach" <> wrote:
    >> * :
    >>> 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?
     
    Alf P. Steinbach, Feb 22, 2008
    #12
  13. * Alf P. Steinbach:
    > * James Kanze:
    >> On Feb 21, 10:20 am, "Alf P. Steinbach" <> wrote:
    >>> * :
    >>>> 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?
     
    Alf P. Steinbach, Feb 22, 2008
    #13
  14. James Kanze Guest

    On Feb 21, 3:45 pm, wrote:
    > On 21 Feb, 09:20, "Alf P. Steinbach" <> wrote:


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

    --
    James Kanze (GABI Software) email:
    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
     
    James Kanze, Feb 22, 2008
    #14
  15. James Kanze Guest

    On Feb 22, 10:18 am, "Alf P. Steinbach" <> wrote:
    > * James Kanze:
    > > On Feb 21, 10:20 am, "Alf P. Steinbach" <> wrote:
    > >> * :
    > >>> 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:
    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
     
    James Kanze, Feb 22, 2008
    #15
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Fred
    Replies:
    1
    Views:
    602
    Neredbojias
    Sep 26, 2005
  2. John Harrison

    using templates in templates

    John Harrison, Jul 31, 2003, in forum: C++
    Replies:
    8
    Views:
    385
    Torsten Curdt
    Jul 31, 2003
  3. JKop
    Replies:
    3
    Views:
    477
  4. recover
    Replies:
    2
    Views:
    809
    recover
    Jul 25, 2006
  5. Marco Nawijn
    Replies:
    1
    Views:
    164
    Victor Bazarov
    Jan 29, 2013
Loading...

Share This Page