Board index » cppbuilder » Class-dependent decision making???

Class-dependent decision making???


2003-10-16 11:22:16 PM
cppbuilder83
I suppose this belongs in the .writing group, so...
Okay, I want to convert between some unspecified class, T (which may or
may not be based on a VCL class), to an AnsiString. I plan to do this
in one of three ways depending on the class:
(1) for classes that can be directly cast to AnsiString, just cast them.
(2) for classes that cannot be case directly and that I didn't write
myself, I'm creating a set of overloaded functions ConvToString(T
Variable) to do the conversion.
(3) for (perhaps complex) classes I write myself, I've created a special
TTextRep class that can be used to convert my own classes into a string
representation.
The reason I created my TTextRep class was so that it could use the
methods listed above to convert all the pertinent members of a given
class (which that class "registers" with its own instance of the
TTextRep calls) to their text representations and combine them into a
string that represents an instance of the given class. I can post more
info about how I do this for classes outside the VCL if you like, but
that's not the meat of my question here...
Here's the question:
How would you write code that (given an object of some class, T) can
figure out which of the three methods listed above it should use?
My first thought was to consider some form of template function such as
"GeneralConvToString(T Var)." But how would you ask "does 'ReturnString
= (AnsiString)Var' work? If not, does Var have a member of type
TTextRep? And if not, then try ConvToString(Var) and throw an exception
if that fails." ???
The other option I've thought of was to use a flag (probably an enum) so
that when a class of mine registers its pertinent members with a
TTextRep, it also provides a flag to say "this member should be
converted to an AnsiString using method number IConvMeth."
Any other ideas? Thanks for any input!!!
-Jason
 
 

Re:Class-dependent decision making???

"Jason W. Hinson" < XXXX@XXXXX.COM >wrote in message
Quote
(1) for classes that can be directly cast to AnsiString, just cast them.
What kind of classes exactly? AnsiString cannot be derived from, so any
classes that are going to support such a cast would need to implement a
specific conversion operator for that purpose.
Quote
(2) for classes that cannot be case directly and that I didn't write
myself, I'm creating a set of overloaded functions ConvToString(T
Variable) to do the conversion.
Since you did not write the classes yourself, you are requiring the class
authors to somehow plug into your code for class-specific formatting,
correct? Perhaps by creating their own TTextRep descendant classes?
Quote
How would you write code that (given an object of some class,
T) can figure out which of the three methods listed above it should use?
You can't, especially since you don't have access or control to the classes
you did not write yourself. Not if you're trying to process classes in a
centralized fashion. You would have to pick a single approach that all
three scenerios would share, which would probably be the approach to
register TTextRep-derived classes. Then you only have to write code for
TTextRep, and if you want to support a new class that you did not write
yourself, simply write a new TTextRep decendant for it (or let the author
write it if he wants to support your system).
Quote
My first thought was to consider some form of template function
such as "GeneralConvToString(T Var)." But how would you
ask "does 'ReturnString = (AnsiString)Var' work?
You can't. However, if you wrote such a function that simply performed the
cast, then you tried to pass in a class that did not actually support the
class, the compiler would fail to compile the code with an error saying as
much. Or, are you referring to a runtime system where you don't know at
compile-time which classes are being included in the project?
Quote
If not, does Var have a member of type TTextRep?
Again, not possible, unless the code is written to specifically query the
class for such information in a standard manner, and the class itself
supports such a query. For example, in COM, all interfaces derive from the
same IUnknown interface, which in turn has a standard QueryInterface()
method for querying a given object if it supports a particular subinterface.
Quote
And if not, then try ConvToString(Var) and throw an
exception if that fails." ???
In short, what you're asking for requires your code to have intimate
knowledge about every object it is going to work with. But that is not
actually the case since you want to support objects that you did not write
yourself. So you must have some kind of abstraction layer that hides the
particular details of each object from your code, and have your code work
with the abstraction layer, not the objects directly. Then you can plug in
new classes by writing wrappers for them that are compatible with the
abstraction layer, whichin this case sounds like your TTextRep class. That
is how all plugins work in general, that is the only way they can work.
There has to be some kind of common functionality between what the
application is expecting and what the end-user's objects actually do.
Quote
The other option I've thought of was to use a flag (probably an enum)
so that when a class of mine registers its pertinent members with a
TTextRep, it also provides a flag to say "this member should be
converted to an AnsiString using method number IConvMeth."
That will only work with your own classes, since only your classes would
know about the flag. That would not apply to other classes you did not
write, unless they are wrapped first in something that is more compatible
with your code. Which then gets back into what is described above.
Gambit
 

Re:Class-dependent decision making???

Hi again, Gambit, and thanks for the reply...
It seems I may have made things a bit confusing by not fully explaining the
situation. Let me try to clarify. In the end, I may have to write wrappers for
EVERYTHING I use, as you suggest, but let me explain a little better my current
thinking:
As I've noted elsewhere, I want to be able to take classes that I create and
rather easily provide a method for representing that class as a string (at least
representing enough info to duplicate an instance of that class with all the
pertinent information).
I've decided to tackle this by creating a TTextRep class (decendent from a
TStringList). Every class I create will have a member instance of the TTextRep
class, and it will register its pertinent member variables with its own TTextRep
upon instantiation. The TTextRep member will then be able to construct the text
representation of the class from its current settings, fill the pertinent
variables of the class from a provided string representation, stream the string,
etc. To do that, TTextRep has the following attributes:
* TTextRep has a template function to register a variable of any type. This
function takes the variable and its AnsiString name (actually, I just pass the
raw name to a macro that produces the function call with ##name,#name) and
creates a "Name=Value" string to add as a member of its inherited TStringList.
It also creates an instance of a TObject-based template function (TVarObj),
where TVarObj's main purpose is to hold a reference to whatever type of variable
was being registered. The "Name=Value" string and TVarObj object are added to
the list with AddObject.
* TTextRep has a long list of static "CovertTo/FromString(<someclass>X,
AnsiString S)" overloaded member functions that can take TFonts, bools, various
maps, etc, etc, and convert them to/from AnsiStrings. These are all classes
that I didn't create, but I produce a way to generically convert them to/from an
AnsiString.
* TTextRep has properties to return the string representation of its owner or
to set the variables of its owner (via Objects[i]->pRefToVar) from a string
representation.
Now, a major chore is thus to create all the overloaded "ConvertTo/FromString"
functions, but even then I still have another problem to deal with:
Say that class TA has a TTextRep member and class TB has both a TTextRep member
and a member of type TA. The TTextRep class needs to know how to convert a
TA-type object to string when TB registers its TA object. But I since the
TestRep.h file is in the A.h header, I can't have A.h in the TextRep.h header,
which I need if I want to write, for example, ConvertToString(TA X, AnsiString
S), right?
My Solution: When I register a variable, I'll include a flag that notes whether
the variable has a TTextRep member. I can still pass the class to TextRep
because I'm using a template function to do the passing, not an overloaded
function. Then, when text rep goes to create the text representation of that
variable, it will call another template function:
template <class T>void TTextRep::ConvertToString2(T Var, AnsiString Str)
{
Str = Var->TextRep->StringRep;
}
or it can call another template function to convert from a string:
template <classT>void TTextRep::ConvertFromString2(T Var, AnsiString Str)
{
// does the conversion and sets the members
// of V to values provided in Str
Var->TextRep->StringRep = Str;
}
I think this should work. In the end, while writing a new class, adding very
few lines of code will provide it with a very simple way to convert to/from a
string. Thoughts.
-Jason
 

{smallsort}

Re:Class-dependent decision making???

"Jason W. Hinson" < XXXX@XXXXX.COM >wrote in message
Quote
It seems I may have made things a bit confusing by
not fully explaining the situation.
Yes, please always include full details, and specific examples if possible.
Quote
As I've noted elsewhere, I want to be able to take classes that
I create and rather easily provide a method for representing that
class as a string (at least representing enough info to duplicate an
instance of that class with all the pertinent information).
In other words, you want to reproduce everything that the DFM system already
does.
Quote
* TTextRep has a template function to register a variable of any type.
This function takes the variable and its AnsiString name ... and creates
a "Name=Value" string to add as a member of its inherited TStringList.
It also creates an instance of a TObject-based template function
(TVarObj), where TVarObj's main purpose is to hold a reference to
whatever type of variable was being registered. The "Name=Value"
string and TVarObj object are added to the list with AddObject.
That sounds like a lot of overkill. Since the containing class already
knows its specific properties and data types, why can't it just stream the
string values directly? You're descriptions are very long, and your
examples very few, so I am getting lost in everything you are trying to
explain.
Quote
Say that class TA has a TTextRep member and class TB
has both a TTextRep member and a member of type TA.
The TTextRep class needs to know how to convert a
TA-type object to string when TB registers its TA object.
Now you're getting into the realm of streaming entire custom classes, which
is vastly more complex then streaming basic types like strings, ints, etc.
And frankly, if you are storing TTextRep as just a class member instead of
an ancestor, I don't see how you're going to be able to pull off recursive
streaming correctly without the present of RTTI for non-VCL classes. Part
of the reason that the DFM system works is because every streamable class
has RTTI available, and that RTTI is exposed from the lowest base class
level - TObject - which all VCL classes derive from. From what I've heard,
you're not going to have that same kind of setup for the classes you want to
work with.
Quote
But I since the TestRep.h file is in the A.h header, I can't have A.h
in the TextRep.h header, which I need if I want to write, for example,
ConvertToString(TA X, AnsiString S), right?
Sounds like a classic header-circular-reference issue, which is always
addressed by using forward declarations. However, I will comment that in
order to keep TTextRep generic, it can't know about any specific class type
(except for maybe common bases), so A.h should not be included in TextRep.h
to begin with, nor should any of TTextRep's methods be declared to expect TA
specifically. Templates are usually the way to go if you want to maintain
generic behavior for specific data types without knowing what the types
actually are.
Quote
My Solution: When I register a variable, I'll include a flag that notes
whether
the variable has a TTextRep member. I can still pass the class to TextRep
because I'm using a template function to do the passing, not an overloaded
function. Then, when text rep goes to create the text representation of
that
variable, it will call another template function:

template <class T>void TTextRep::ConvertToString2(T Var, AnsiString Str)
{
Str = Var->TextRep->StringRep;
}
That will not work if the passed in class does not have a TextRep property
to begin with. The code won't even compile. Plus, in order to work for
those classes that do support TTextRep, the property would always have to be
named "TextRep" in every single class, which reduces flexibility in general.
Even with the flag, I'm not sure that it will work anyway, since templates
are resolved during compile-time, not runtime, so if you pass a class to the
main ConvertToString() function, the compiler still has to resolve
ConvertToString2() for that same class as well, and if the class does not
have TTextRep, the compile will fail regardless of any flags since you're
talking about flags in a runtime sense, not compile-time.
Quote
I think this should work. In the end, while writing a new class,
adding very few lines of code will provide it with a very simple
way to convert to/from a string.
I still think you are going about everything the wrong way to begin with,
especially for the level of compatibilty with multiple frameworks that you
are asking for. I do not have a better solution in mind for you at the
moment, it will take some time to think about. It will probably have to
wait until the weekend. In the meantime, I would suggest that you post some
real examples of actual (simple) classes that you want to work with, both
VCL and non-VCL, along with the associated string outputs, so we can get a
better understanding of what exactly you're aiming for.
Gambit
 

Re:Class-dependent decision making???

Hi again, Gambit...
<sigh>Okay, you're right my idea isn't quite going to work. Specifically, you
pointed out a major flaw in my thinking when you noted that even if a template
function only makes a given call when a specific flag is set, that call has to
be valid for any variable I ever send to the template function regardless of
what flag I include in the call.
In any case, I'm still trying to meet my dream of
(1) converting any of my custom classes to/from a string (to the extent that I
can re-create an instance of the class with info in the string).
(2) doing this with a fairly consistent mechanism that makes it as easy as
possible to add to any of my classes.
(3) being able to do this with properties based on types that the DFM doesn't
know how to stream such as maps, my own TObject-based (not TPersistant-based)
classes, etc.
(4) include text in the string for ALL the necessary properties EVEN if they are
set to default values (something the DFM doesn't do).
By the way, is there a good reference for a list of types/classes that the DFM
handles properly?
Thanks.
-Jason