[Exceptional C++ Style] Fw: const data - was Item 17

Jon Jagger jon at jaggersoft.com
Thu Dec 30 05:36:51 EST 2004


Kevlin Henney wrote:

> In message <41D3231F.5070109 at jaggersoft.com>, Jon Jagger 
> <jon at jaggersoft.com> writes
>
>> Kevlin Henney wrote:
>>
>>>> This depends on the language. In C# (CLR) the proxying facilities 
>>>> allow interception. I could intercept field access if I wanted to. 
>>>> And yes I do mean fields and not properties (of course I could 
>>>> intercept properties too if I wanted). There would be no change 
>>>> required to the users code.
>>>
>>>
>>> True, but the representation of the field must still exist for it to 
>>> be intercepted, and anything much beyond operationally as opposed to 
>>> functionally related extension of semantics might be considered 
>>> surprising unless that is an accepted part of the architecture.
>>>
>> I can use public fields, public properties, or public getter methods. 
>> Whichever I choose the representation of the chosen thing must still 
>> exist and (in the CLR) all three have scope for extension. Surely any 
>> operational extension would be pretty much equally acceptable in all 
>> three cases and any functional extension would be equally 
>> unacceptable in all three cases?
>
>
> Yes, as I said, anything beyond operational extensions would be 
> surprising, which means functional extensions -- apologies that my 
> phrasing was unclear.
>
>> These public fields are in a class and the class _does_ have an 
>> interface. But clearly the fields are not in the interface. And 
>> neither are any properties. So what is in the interface? Answer, an 
>> indexer. You provide a string name and the subclass provides the 
>> non-terminal with that name. Why do I have this? Because it proved 
>> wonderfully useful in XML driven test code (for example).
>
>
> I'm confused. Clearly public fields and properties _are_ in the 
> interface, and adding an indexer simply extends the public interface. 
> What have I misunderstood in your phrasing?

I mean interface as in the keyword interface. Which are not allowed true 
fields. Viz, simplifying...

public interface IGrammar
{
    NonTerminal this [ string name ] { get; }
}
public class JavaGrammar : IGrammar
{
    public readonly NonTerminal CompilationUnit;
    //lots more like that

    public NonTerminal this [ string name ]
    {
        get { ... }
    }
   
    public JavaGrammar()
    {
          CompilationUnit = new NonTerminal("CompilationUnit", 
...............);
          lookup.Add("CompilationUnit", CompilationUnit);    
          //lots more like that
    }

     private readonly HashMap<string, NonTerminal> lookup;
}

This allows me to have a general purpose parse tester which accepts an 
IGrammar object and an XML file containing elements specifying the input 
string, the name of the nonterminal to parse it and the expected outcome 
of the parse. The indexer allows dynamic access to the NonTerminal given 
its name in a string.

>
>> The natural syntax is, I still maintain, field-like access.
>
>
> Not when the natural mode of access is not field based. 

The natural mode of access of _what_? Of everything? ...

> In an OO language without properties or parentheses for argumentless 
> methods, the 'natural' syntax is to use access that looks method based. 

...The way I think of it is that it's the . or -> operator that provides 
the "access" and the thing on the other side of the . or -> is what's 
being accessed. If the thing being accessed clearly requires some 
abstraction (for whatever reason) it's clearly appropriate to use a 
method. What I'm questioning is the "universal" application of that. As 
you pointed out using public fields in this case has not caused any 
problems (partly because they are immutable and partly because the class 
contains nothing else). I agree you cannot predict the future so it's 
often wise to take precautions. But equally you must limit your design 
and not try to make it all things to all men. It's a fine line; a 
balance between the present and the future.

Ultimately, as always, it boils down to a choice. I could use get 
accessors in the class, declare those as operations in an interface, and 
program to the interface. This would, as you say, allow me handle-body 
if I wanted to. The problem is I don't want to. And I cannot think of a 
reason why I'd want to.

If you can think of any reasons why I'd want to handle-body I would be 
very interested to hear them. Remember the grammar does nothing. It 
really is just data. There are classes that traverse grammar objects (eg 
parsers). These use interfaces and have several implementations (eg ones 
that generate events as they parse and ones that don't). My feeling is 
that any extensions I'd want to do would not be on the grammar itself 
but on the objects using the grammar.

Of course, having said that it's odds on that tomorrow I'll want to 
handle-body the grammar ;-) But let's consider that. Suppose I found I 
did want to handle body it. I could create a C++ interface with getter 
operations and a C++ class implementing it etc. (And the C++ class would 
ultimately probably delegate to the class containing just the public 
data members.) I'd then face the prospect of having two sets of clients; 
those that used the grammar objects directly (field access), and those 
that used the grammar objects indirectly (getter access). And I would 
then naturally think about unifying the two sets of clients. But 
wouldn't that be a good time to think about it? If I unified I'd be 
forcing the field access clients to change. Why do that if the reason 
for handle-bodying is not applicable to a large set of those clients? I 
might decide not to unify; to retain field access via one class and 
introduce getter access via a new interface?  Wouldn't this be an 
example of the implementor taking a hit on complexity to make things 
simpler for (some of) the clients? Why must one size fit all? Why not 
adapt as needed? Not that the extra complexity would be very much; as I 
said - the grammar class is generated by another program so writing more 
generator clients would be simple.

Cheers
Jon





More information about the Effective-cpp mailing list