[RESOLVED! (thanks ToohrVyk)] chicken & egg design problem

Started by
17 comments, last by Verg 18 years, 8 months ago
The kind of problem that makes the best of us resort to late-night refrigerator-punchings [grin] Say you have a simple data type that is platform-specific, e.g.

COLORREF
There is no way to express this data type as null, because 0 is just as valid as 0xFFFFFFFF (-1) for it. Our base class object (call it an EditControl) is the interface for all EditControl implementations (Win32, *nix, Mac, etc...). When creating an "EditControl", we use an inner struct EditControl::style, which is a POD that contains style type pointers, and such. One of those should be the background color for the EditControl's implementation. Since we can't express COLORREF as a null or non-existent member, we need to wrap it in a class... and then pass a pointer (of that class) to the EditControl::style for background "Color"... if the default parameter = 0, meaning the pointer to "Color" is zero, we can set an EditControl::style with no default background "Color" member... groovy. Now, lets pretend we need this simple data type (COLORREF) when wrapping some C-based api, namely ::SetBkColor(hdc,color)... We can't extract COLORREF from EditControl::style's "backgroundcolor" member, because EditControl::style lives at the base-class level (EditControl), not the implementation level (Win32EditControl)... What does that mean? It means that EditControl::style doesn't know "COLORREF", nor can it return "COLORREF" to us. ---------------------- Now you're all thinking... hmmm... template-ize that mother.

//inside class Edit control...

template<typename ImplementationType>
struct style{

public

    ImplementationType backgroundcolor() { return backgroundcolor; }
private:

    ImplementationType backgroundcolor;

};

// later, somewhere else...

style<COLORREF> bgcolor;

That's fine; but here's the complication... ...we need to be able to pass these "style" structs around in pointers, preferrably smart pointers... why? So they get deleted when no longer referenced, for starters...


// inside EditControl class...

EditControl(style_ptr_type s); // constructor prototype

typedef Loki::SmartPtr<style>     style_ptr_type;   // a HA! Doesn't work!!
That doesn't work... because we don't know the template parameter of style yet; it's not a "real" type. So, if we want to templatize "EditControl::style", we can't know its type, and thus can't pass it into the EditControl constructor... which means the style struct is useless to us! ---- Template specialization is not really a help, either...

//inside a "Color" class

template<class DerivedType, class ImpType>
ImpType type(DerivedType &d);

// Color is declared a friend of it's derived type, Win32Color...

// specialization

template<>
COLORREF Color::type(Win32Color &c)
{
    return c._bgcolorref;
}
That would be OK... IIIIIF... EditControl::style had pointers to Win32Color, instead of pointers to Color... This ugly hack is out of the question!!

// ay ay ay!!!

_pstyle->_pColor->type(*(static_cast<Win32Color *>(GetImpl(_pstyle->_pColor))));
---------------------- What this code has done is basically left me numb [smile]... and wanting a different approach... How common is this design problem in OOP? This may be a crazy design; but I'd like something that kept the abstraction layer (Base class) and implementation layer (Concrete class) separate... I don't want to identify "COLORREF" as the value inside a "Color" class... but need to get a "COLORREF" somehow so the Win32 APIs will be available. I have to step back and let the world swirl by for a while... later... and thanks [imwithstupid] [Edited by - Verg on July 28, 2005 12:57:32 PM]
my_life:          nop          jmp my_life
[ Keep track of your TDD cycle using "The Death Star" ] [ Verge Video Editor Support Forums ] [ Principles of Verg-o-nomics ] [ "t00t-orials" ]
Advertisement
If you use a pointer to a COLORREF, you can set it to NULL for undefined.
Quote:Original post by bytecoder
If you use a pointer to a COLORREF, you can set it to NULL for undefined.


Yeah... but then EditControl::style would know "COLORREF" (or "COLORREF *")... and EditControl::style should be able to take any implementation's Color class, since it lives at the abstraction level... away from Win32 types.
my_life:          nop          jmp my_life
[ Keep track of your TDD cycle using "The Death Star" ] [ Verge Video Editor Support Forums ] [ Principles of Verg-o-nomics ] [ "t00t-orials" ]
I'm not sure I understand exactly, but I think in many cases the style template definition would need to be contained in a header which has been #included. My impression was that when you say:

style

it has to already have seen the defition of syle (which... in more abstract terms, doesn't exist. I mean the moment you try to define 'style', you know you don't have any...)

So, I'm pretty sure you can't simply forward declare a template and then typedef it or point to it, if that's what you're trying to do.
that should be:

style ( COLORREF )

where ( ) are greater than / less than signs. Damn gdnet html parser.
Quote:Original post by Anonymous Poster
I'm not sure I understand exactly, but I think in many cases the style template definition would need to be contained in a header which has been #included. My impression was that when you say:

style

it has to already have seen the defition of syle (which... in more abstract terms, doesn't exist. I mean the moment you try to define 'style', you know you don't have any...)

So, I'm pretty sure you can't simply forward declare a template and then typedef it or point to it, if that's what you're trying to do.


Sort of; I realize you can't get a pointer to an undefined type, or even typedef a pointer to an undefined type... if "style" was a template, you'd need a solid template parameter to create a type of "style"...

But because "style" lives in an abstract (Base) class, we don't know any specifics; the implementation isn't set... so nothing like "COLORREF" is known at the base class level, because we need the base class to remain abstract. Why? So it can be useful for any implementation; not just Win32.

...

My frustration isn't not understanding why it doesn't work; but not being able to pull back and design something that would actually work :)

Thanks, though.
my_life:          nop          jmp my_life
[ Keep track of your TDD cycle using "The Death Star" ] [ Verge Video Editor Support Forums ] [ Principles of Verg-o-nomics ] [ "t00t-orials" ]
For something like this [er, exactly this actually] I made a 'reference' color type, which is what the UI always refers to, and then create platform specific conversion functions from/to the base type to/from the platform specific color type. [edit: similarly in principle if not code to how you extend the base EditControl into a platform specific type]

Seems to work well enough. Saves a bit of headache in development if people stick to the 'base' type. Something like this is also useful for fonts, text, and rectangles.
Quote:Original post by Anonymous Poster
that should be:

style ( COLORREF )

where ( ) are greater than / less than signs. Damn gdnet html parser.


lol...

Yep... check my first post again; it's in there.

The problem is, I need to do THIS:

typedef Loki::SmartPtr<style>  style_ptr_type;//so that the EditControl can be constructed with a "style" EditControl(style_ptr_type s);  //c'tor prototype


Yeah; I could exclude the style pointer type at the base level, and include it at the implementation level:

Win32EditControl(Loki::SmartPtr<EditControl::style<COLORREF> > s); // yuck!

That's not exactly very elegant though [smile]
my_life:          nop          jmp my_life
[ Keep track of your TDD cycle using "The Death Star" ] [ Verge Video Editor Support Forums ] [ Principles of Verg-o-nomics ] [ "t00t-orials" ]
Quote:Original post by Telastyn
For something like this [er, exactly this actually] I made a 'reference' color type, which is what the UI always refers to, and then create platform specific conversion functions from/to the base type to/from the platform specific color type. [edit: similarly in principle if not code to how you extend the base EditControl into a platform specific type]

Seems to work well enough. Saves a bit of headache in development if people stick to the 'base' type. Something like this is also useful for fonts, text, and rectangles.


Did you use macros, then?

#ifdef _WIN32
#define REFCOLORTYPE COLORREF
#elif defined(_MAC)
#define REFCOLORTYPE somethingelse
#endif

I guess macros might work; but they're pretty inflexible.

Maybe I misunderstand you though.
my_life:          nop          jmp my_life
[ Keep track of your TDD cycle using "The Death Star" ] [ Verge Video Editor Support Forums ] [ Principles of Verg-o-nomics ] [ "t00t-orials" ]
I actually didn't use macros [as they are inflexible, and hard to debug, and annoying].

I declared an entirely new base class. The [base] UI controls always use that color class when a color is referred to. In the platform specific UI code, the base color class is converted as necissary to the platform's color.<br><br>so:<br><br><pre><br><br>struct base_color{<br> // generically define color, probably ARGB ints, or something similar.<br>};<br><br>struct base_UI{<br> void setcolor(base_color)=0;<br>};<br><br>struct win_UI{<br> void setcolor(base_color){<br> setcolor( convert_to_native (base_color) );<br> }<br> void setcolor(native_color){<br> // windows specific color setting.<br> }<br>};<br><br>struct mac_UI{<br> // similar<br>};<br></pre><br><br>Not the most elegant thing in the world, and someone with more mojo could probably take a lot of the typing out of this… but it works, and leaves a lot of the implimentation open for inevitable porting design changes.<br><br>[edit: looking back at some code, I actually just store the color in the generic form, to avoid too many conversions and too much code duplication. The &#111;nly conversions done are done when the actual platform specific API calls are made]

This topic is closed to new replies.

Advertisement