The Most efficient way to start this design

Started by
9 comments, last by Bagpuss 20 years, 6 months ago
First of all, apologies, this is not focussed on game development, but I have found that there is a wealth of good advice in this forum, so if I can impose I''d like any thoughts on the project I have to work on below. I have to write an aplication to view files created using a certain transfer protocol. (so we can debug / check that some other software is producing the correct output). Looking at the specs, this protocol has 27 different data types. These 27 types are effectively variants on Byte, Float and char. Some of them are pretty basic like a 32 bit int (can treat as a 4 byte number), and string, but others have certain constraints attached (they might insist on only using certain characters, or a maximum number range) Would I be better defining these as 27 structs, and writing some supporting code to ensure that each is validated as it is assigned to the struct?, or creating 27 classes, allowing each class to ensure that as part of it''s constructor it can except if the data type is not correct ? Or would I be better off again storing them internally in a language type (probably C++), and just write 27 validation functions, and 27 conversion functions to make them look correct for display ? My last thought for doing it would be to make a template class, that will validate the type to be correct, and return a variable of the correct type, but as there are different rules for all the variations, I suspect that a template won''t really save me a great deal of work. Any ideas / comments most welcome here, as I want to make sure I start on this design only once ! Cheers, Bp
Advertisement
Templates will save on typing for sure. But this problem is quite amenabdle to OOP, so better to use standard class hiearchy and inheritance as you get more band for your buck.

Here is a proposed design:
enum DATATYPE_SIG   //all datatypes have unique sig defined here, not the invalid sig is important {           SIG_INVALID,        SIG_CHAR,        SIG_FLOAT,        SIG_STRING_32_CHARS};struct Data_Type_Base{    virtual DATATYPE_SIG    get_type(){ return SIG_INVALID; }       //defaults to invalid type    bool            validate    ( void )    {        get_validation_factory()->validate(this);                   //use singleton validation factory..    }    virtual bool    seralize    ( OutputStream& r_stream_out );     //inheritable seralzie functions    virtual bool    seralize    ( InputStream&  r_stream_in  );     //inheritable seralzie functions}struct Data_Type_Char : public Data_Type_Base{    virtual DATATYPE_SIG    get_type(){ return SIG_CHAR; }          //each derived class must define their own get_type    virtual bool    seralize    ( OutputStream& r_stream_out );    virtual bool    seralize    ( InputStream&  r_stream_in  );    unsigned char m_element;}struct Data_Validator_Base          //base class for double dispatching note all the elements must have a validate_data func{    virtual bool    validate_data   ( Data_Type_Base* p_data){ return false; }    virtual bool    validate_data   ( Data_Type_Char* p_data){ return false; }};struct Data_Validator_Char : public Data_Validator_Base{    virtual bool    validate_data   ( Data_Type_Char* p_data )      //only defines funciton which it is intreseted in    {         //simple test for 0 element value to be invlaid, you would ofcoruse have your own, this is just an example        if (p_data->m_element==0)              return false;        return true;     }};class Validation_Factory{public:        bool    validate    ( Data_Type_Base* p_data )    {        assert(p_data!=NULL);        DATATYPE_SIG sig = p_data->get_type();        if (m_validators.find(sig) == m_validators.end())        {            assert(0);            return false;        }        return m_validators[sig]->validate_data( p_data );          //visitor pattern to dispatch to the correct validator    }private:        Validation_Factory();                                           //constructor is private only can be created by friend function    std::map<DATATYPE_SIG,Data_Validator_Base*>    m_validators;    //map of type->validators, loose coupling allows for quickly changing valditoar to data type        friend Validation_Factory*  get_validation_factory(void);};Validation_Factory*  get_validation_factory(void)                   //singleton pattern{    static Validation_Factory factory;    return &factory}


There ya go, ive noted alot of patterns im using. The main strengths of this design is the loose coupling of validator to datatype. Also the use of the vistor pattern to correctly dispatch the approraite datatype to the validator without using static cast.

BTW : anyone know what the tags are for making a source block?

-ddn


[edited by - ddn3 on October 9, 2003 9:23:59 PM]

[edited by - ddn3 on October 9, 2003 9:25:21 PM]

[edited by - ddn3 on October 9, 2003 9:26:54 PM]

[edited by - ddn3 on October 9, 2003 9:35:11 PM]
Thanks, sorry for not posting sooner, been struggling with my network connection.

So using your code, all I need to do is add code to create an instance of each of my 27 types to be validated, and stick them all in the Map ?

Thanks again,

Bp
Yes, just define an interface for adding validator objects into the valadaiton factory. I would let the factory own them, so when the factory dies, it deletes the valadators too.

Hope it works out for you.

-ddn
To clarify, you need to :

-add the additioanl enums
-add the dervied data types with the approrirate functions
-add into the validator base the approriate dispatch function
-add the derived validator types
-add an interface to the validator factory to register validators.

and that should be it. Its alot of typing, but 27 unique data types is alot. I tried to make it more managble by automating the dispatching and rewritting redundant code. You just need to code up the verify function, and seralization functions.

It might be possible to code up a template to do most of this for you and you can pass into the template a function which is both verify/seralize.

Good Luck!

-ddn
OK, I have written it as I think it ought to be written,
but when I attempt to set up the map, I have the line

m_validators.insert(DIC_UL,mUL); (mUL is an istance of the DIC_UL validator type)
and I get an error claiming that DIC_UL cannot be converted into std::_tree<>::Iterator.

I assumed (obviously wrongly) that as an enum was basically treated as an int, that there wouldn''t be a problem with this. So te next question is, how do I get round it ?

Bp


enums are treated differently between different compilers. I know in MSVC 6.0 it treats enums as ints, but in C# enums are their own type and can''t be converted to ints without static casts.

But thats not what is happening here. The insert function for a map has serveral forms.

The form your using expects a range, so its a map::iterator begin -> end.

If you want to insert a single element into the map using the insert function it shoudl be

typedef std:air<DATATYPE_SIG,Data_Validator_Base*> _Validator_Pair;

m_validators.insert(_Validator_Pair(DIC_UL,mUL));

i typedef the validator pair so its easier to read but you dont have too..

or you can use the more convient, but slightly less direct form :

m_validators[DIC_UL] = mUl;

which does assignment and creation.

mUL should be a ptr to a validator object, btw.

-ddn
Cheers, that sorted it.
OK, I have just started to add in my validation code into my funciton stubs, but whenever I run it, no matter what the datatype, and which validator the pointer points to, the software always uses the Data_Validator_Base() function, and so returns false.

As I understood it, if a container was made up of objects that inherited from a base, then the correct object instance would be called, not the base one? This doesn''t seem to be hapenning.
Ill look into it. According to the c++ standards it should use the best matching function to call. But it might be be the case that when the ptr is passed into the valaidation factory it is now a base ptr. Hmm..

-ddn

This topic is closed to new replies.

Advertisement