Re: type traits and element specific functions: design problem
Andy wrote:
I am faced with a basic problem. Currently we only plan to support a
UNIX terminal based character-based UI. The whole set of dialog strings
or prompt strings which I am supposed to throw at the user is quite
large. As of now, each of these has been given a numeric ID and mapped
from a message catalog using catopen / catgets calls.
Now each of these dialogs is a request to the user for an input. There
are well-defined rules as to what is acceptable and what is not
acceptable as part of the input in response to each Dialog.
In other words, each Dialog Id has associated with it a validation rule
for the input that is read in response to that Dialog.
Now it is not difficult to create a set of Input Validator classes,
each doing one kind of validation. Some do validation on date time
strings, others on File names and directories, and still others on
numeric values. We have been able to keep the number of such generic
validators to 8 at present.
e.g.
class DateInputValidator : AbstractInputValidator
{
public:
bool validate ( std::string& ) const
{
...
}
...
};
Now let us suppose that each of my (say) 250 Dialogs can be validated
with one of these 8 validators.
Now my Dialogs are not first class C++ objects. They are merely numeric
Ids. On the other hand, the knowledge of which validator to apply to
the input for a dialog should be with the Dialog. A validator need not
know which dialogs it should validate. But since the Dialogs are not
first class C++ objects, we cannot make give them the intelligence.
In that case, you need some sort of map.
One simple solution, if the dialog id's don't have to be
contiguous (and they don't have to be with Solaris' message
catalogs), is to allocate them in blocks, according to the type
of input validation needed. Thus, for example, dialog id's from
1-1000 require dates, from 1001-2000 filenames, etc. It's a
hack, but it's a time proven one, and you recover the necessary
validator by simply using dialogId/1000 as an index into an
array of validators.
More elegant and cleaner would be to use a std::map< int,
AbstractInputValidator* >, initialized with all of the mappings.
Presumably, each distinct validator is a static variable. In
that case, you could make the map a singleton, with the
constructor of the validator responsible for registering itself
for all of the message types it can handle. Alternatively, in
this case, I think there's a good argument for keeping the
information separately, in a text file with lines like:
messageid valiatortype
A simple AWK script could then generate both the enum for the
message id's and the initialization code for the map. (If the
AWK script takes care of assigning the numeric values for the
symbolic message id's, you could actually use a simple
AbstractInputValidator[] as the map. This is probably the way
I'd go.)
So I am wondering how to approach this problem.
I tried the following approach:
Create a dialog_traits class template. For each dialog Id,
specialize it and define a typedef in the specializtion which
refers to the type of the validator appropriate for this
dialog. Something like:
template <int DLG_ID> struct dialog_traits;
...
template <>
struct dialog_traits<Dialog_Which_File> // say Dialog_Which_File == 5
{
typedef FileNameInputValidator validator_t;
};
- This still creates a pretty large number of template classes - one
for each validator and the executable size can increase significantly
because of this.
The template itself won't cause code bloat, since it has not
code.
The problem is that it requires static resolution. I'm not sure
that this is what you want: your function will be passed a
dialog id as a parameter, I would imagine. Otherwise, you will
get code bloat, since you will need a different function for
each dialog.
Of course, if you do decide on something like this, you could
use the AWK script to generate the specializations.
- This seems unintrusive and elegant.
Not to me. To me it seems more a case of using templates at any
price, even when they aren't appropriate and don't bring any
advantage. In this case, dynamic dispatch seems significantly
more appropriate (and of course avoids any code bloat).
Unless you adopt my first suggestion, you're going to have to
maintain the mapping message id to input validation by hand.
Putting it in a single file, with two fields per line, is
probably the simplest solution with regards to maintenance; you
can even leave it to a non programmer. Burying the mapping in
the syntax of explicit template specialization seems like one of
the most complex and painful.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
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
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]