specifying interface without definition?

Discussion in 'C++' started by Jacek Dziedzic, Dec 5, 2005.

  1. Hello!

    Suppose I'm writing a library to write a binary representation
    of some data to a stream. Let's say that to make it extensible
    in the future I'm preceding the actual data with some info
    like the number of fields, total length, etc. that make up
    some kind of header, assuming the actual data may change
    over time.

    I was hoping to implement a 'class datafile' that would
    represent the whole datafile, a 'class record' which would
    consist of a header and the actual data. I want the 'record'
    class to have a member to write it to a stream, like

    record::write(ostream &out) {
    // write record number
    // write other stuff like sizeof(actual_data)
    // write actual_data
    }

    I was aiming for a design that would allow the class
    actual_data to vary, ie. NOT be defined in the library
    I'm writing, but be defined in the program that uses
    the library. Then I would write programs that would
    define their own actual_data class and use the library
    to store records into a datafile.

    To make the library independent of actual_data I
    have decided to only store a pointer to it, so the
    record class would look like this
    class record {
    // some header data
    actual_data *content;
    }

    Now, it turns out that if actual_data is non-POD then
    I must provide some means to serialize it, like overloading
    << and >> or providing some save_to_bytes()/restore_from_bytes()
    members. These would be used in record::write during
    'write actual_data'. Of course these would have to be supplied
    by the program using the library, as they depend on the precise
    content of actual_data.

    The problem is -- how do I convince the library that I can
    call these functions using -> on the pointer to actual_data?
    Obviously the library doesn't know what actual_data looks like...

    So is there any way to tell the compiler "OK, I have a class
    called actual_data that has some fields you don't know in
    advance, so I can't give you a definition yet, but it definitely
    has some get/put methods so you'll know how to store and restore
    it?"

    It would also be useful if actual_data could pass some info
    to the library, like a 'my_size()' member function, but the
    same problem occurs.

    So how do I tell the compiler that I have a class that supports
    some interface without revealing the whole class definition?

    thanks in advance,
    - J.
    Jacek Dziedzic, Dec 5, 2005
    #1
    1. Advertising

  2. Jacek Dziedzic

    mlimber Guest

    Jacek Dziedzic wrote:
    > Hello!
    >
    > Suppose I'm writing a library to write a binary representation
    > of some data to a stream. Let's say that to make it extensible
    > in the future I'm preceding the actual data with some info
    > like the number of fields, total length, etc. that make up
    > some kind of header, assuming the actual data may change
    > over time.
    >
    > I was hoping to implement a 'class datafile' that would
    > represent the whole datafile, a 'class record' which would
    > consist of a header and the actual data. I want the 'record'
    > class to have a member to write it to a stream, like
    >
    > record::write(ostream &out) {
    > // write record number
    > // write other stuff like sizeof(actual_data)
    > // write actual_data
    > }
    >
    > I was aiming for a design that would allow the class
    > actual_data to vary, ie. NOT be defined in the library
    > I'm writing, but be defined in the program that uses
    > the library. Then I would write programs that would
    > define their own actual_data class and use the library
    > to store records into a datafile.
    >
    > To make the library independent of actual_data I
    > have decided to only store a pointer to it, so the
    > record class would look like this
    > class record {
    > // some header data
    > actual_data *content;
    > }
    >
    > Now, it turns out that if actual_data is non-POD then
    > I must provide some means to serialize it, like overloading
    > << and >> or providing some save_to_bytes()/restore_from_bytes()
    > members. These would be used in record::write during
    > 'write actual_data'. Of course these would have to be supplied
    > by the program using the library, as they depend on the precise
    > content of actual_data.
    >
    > The problem is -- how do I convince the library that I can
    > call these functions using -> on the pointer to actual_data?
    > Obviously the library doesn't know what actual_data looks like...
    >
    > So is there any way to tell the compiler "OK, I have a class
    > called actual_data that has some fields you don't know in
    > advance, so I can't give you a definition yet, but it definitely
    > has some get/put methods so you'll know how to store and restore
    > it?"
    >
    > It would also be useful if actual_data could pass some info
    > to the library, like a 'my_size()' member function, but the
    > same problem occurs.
    >
    > So how do I tell the compiler that I have a class that supports
    > some interface without revealing the whole class definition?
    >
    > thanks in advance,
    > - J.


    This is a classic case for templates. Your actual_data class can be a
    template parameter to the datafile and/or record class.

    #include <ostream>
    #include <string>

    template< class ActualData >
    class DataFile
    {
    void SerializeTo( std::eek:stream& os )
    {
    os << actualData_;
    }
    // ...
    private:
    ActualData actualData_;
    };

    struct MyData
    {
    std::string name;
    int id;
    };

    std::eek:stream operator<<( std::eek:stream& os, const MyData& data )
    {
    os << data.name << ' ' << data.id << std::endl;
    return os;
    };

    void Foo()
    {
    DataFile<MyData> myDataFile;
    // ...
    myDataFile.SerializeTo( std::cout );
    }

    Cheers! --M
    mlimber, Dec 5, 2005
    #2
    1. Advertising

  3. Jacek Dziedzic

    Heinz Ozwirk Guest

    "Jacek Dziedzic" <jacek@no_spam.tygrys.no_spam.net> schrieb im Newsbeitrag
    news:8662c$4394b2bb$540a329e$...
    ....
    > I was aiming for a design that would allow the class
    > actual_data to vary, ie. NOT be defined in the library
    > I'm writing, but be defined in the program that uses
    > the library. Then I would write programs that would
    > define their own actual_data class and use the library
    > to store records into a datafile.
    >
    > To make the library independent of actual_data I
    > have decided to only store a pointer to it, so the
    > record class would look like this
    > class record {
    > // some header data
    > actual_data *content;
    > }
    >
    > Now, it turns out that if actual_data is non-POD then
    > I must provide some means to serialize it, like overloading
    > << and >> or providing some save_to_bytes()/restore_from_bytes()
    > members. These would be used in record::write during
    > 'write actual_data'. Of course these would have to be supplied
    > by the program using the library, as they depend on the precise
    > content of actual_data.
    >
    > The problem is -- how do I convince the library that I can
    > call these functions using -> on the pointer to actual_data?
    > Obviously the library doesn't know what actual_data looks like...


    That's (also) what inheritance has been invented for. In your library you
    can define some class, that defines all methods your library needs to call.
    But you do not provide an implementation of those methods. Instead you
    declare them as pure virtual functions. The only function, which you should
    implement, is an empty, virtual destructor. You define this class in a
    header file, which is used both in your library and the programs using the
    library.

    If your library only needs to read and write binary data from/to a stream,
    such an interface yould look like

    class AbstractData
    {
    public:
    virtual ~AbstractData() {}
    virtual int read(istream& stream, char* buffer, size_t bytesToRead)
    = 0;
    virtual int write(istream& stream, char* buffer, size_t
    bytesWritten) = 0;
    };

    A program using your library can define its own class derived from
    AbstractData. It must implement all pure virtual methods of its base class,
    otherwise it would not be possible to create an instance of that class. Any
    decent compiler will take care of that. Such an implementation could look
    like this (I'll leave the actual implementation as an exercise :)

    class ConcretData: public AbstractData
    {
    int read(istream& stream, char* buffer, size_t bytesToRead);
    int write(istream& stream, char* buffer, size_t bytesWritten);
    // ...
    // More stuff, that is not directly used by your library
    };

    If you like it, you can repeat "virtual" for all functions inherrited from
    AbstractBase, but you don't have to. Once a function has been declared
    virtual it will remaon so in all derived classes. That's also why there
    should be an empty, virtual destructor in your base class. Even though there
    is no data, that needs to be destructed, the virtual destructor allows you
    to delete an instance of any derived class through a pointer to your
    abstract base.

    HTH
    Heinz
    Heinz Ozwirk, Dec 5, 2005
    #3
  4. Thanks a lot for the responses. I'd try the template way.

    - J.
    Jacek Dziedzic, Dec 6, 2005
    #4
    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. Matt
    Replies:
    2
    Views:
    842
    Ben Edgington
    Oct 12, 2004
  2. ogtindeed
    Replies:
    3
    Views:
    370
    Chris \( Val \)
    Feb 10, 2004
  3. /M
    Replies:
    1
    Views:
    350
    Gianni Mariani
    Nov 14, 2006
  4. stevenmac2
    Replies:
    1
    Views:
    604
    stevenmac2
    Aug 23, 2007
  5. Andrew Parlane
    Replies:
    11
    Views:
    207
    Andrew Parlane
    Aug 29, 2008
Loading...

Share This Page