how to merge an attribute in multi-inheritence - multiple-inheritance

Repeatedly inheriting from 2 classes having the same parent, I fall into the classic case of inheriting 2 times of the same attribute. I'd like to merge the 2 attributes into one and tried to do it with an undefine, but it gets me a compile error.
The other solution I see is renaming the attribute from one of both parents, but as I understand each instance of my D class would have an useless attribute which is not what I want...
Error: Undefine subclause lists name of frozen feature or attribute or
C external.
What to do: unless you can change the status of the feature in the parent,
remove its name from Undefine subclause since it cannot be undefined.
How to merge 2 attributes from repeatedly inherited classes
class A
serial: STRING
end -- class A
class B
inherit
A
end -- class B
class C
inherit
A
end -- class C
class D
inherit
B
undefine
serial -- error seems to appear here in that case
end
C
end -- class D

In case it's two unrelated attributes (not coming from the same parent) that you want to merge, you should redefine both of them:
class A
feature
serial: STRING
end
class B
feature
serial: STRING
end
class C
inherit
A
redefine
serial
end
B
redefine
serial
end
feature
serial: STRING
end
As you already saw, the compiler will not let you undefine an attribute, even when the goal is to merge it with another attribute.

There is no reason to undefine a feature that is going to be merged with the same version coming from a different inheritance path. In the example, the attribute serial is not changed in B, C, and D. Therefore, inheriting from B and C without any adaptation is OK:
class D inherit
B
C
end

Related

Eiffel: a way to call precursor redefined feature on another function

I'm looking for a way to call Precursor on another feature. The context is that I redefine a routine calling another heir's feature that calls mine, and I'd like to call it explicitly. Is there a way to do something such as in java Precursor.feature_a.
If not, the only alternative I find is to write a feature_a_implementation and call it from redefined feature. Is there a particular reason not to have this mechanism? The consequences of doing that would be to define 2 times the contract of feature_a into feature_a and feature_a_implementation
Feature replication can be used for that:
class A feature
f do print ("A") end
end
class B inherit
A redefine f select f end
A rename f as g end
feature
f do print ("B") end
h do g end
end
Feature f of class A appears in class B twice: under name f (the redefined and selected version) and under name g (the original version). The feature h calls g and prints A as expected.

Eiffel, multiple types conformance: a way to specify that a parameter is a descendent from A and B?

Is there a way (I'm sure there is out of runtime check...) to specify that a parameter or a variable in general conforms to multiple types? to avoid doing something such as
work (a_printer: PRINTER; a_scanner: SCANNER)
do
a_printer.print
a_scanner.scan
-- OR without second parameter
if attached {SCANNER} a_printer as l_scanner then
l_scanner.scan
else
throw RuntimeError
end
end
If feature work belongs to a class that may have formal generic parameters, it could be defined as taking one argument of the corresponding formal generic type:
class X [D -> {PRINTER, SCANNER}] feature
work (device: D)
do
device.scan
device.print
end
end
Then, at the caller site, one could make the call
x.work (multi_function_device)
where x has an appropriate type, e.g. X [MULTI_FUNCTION_PRINTER].
If work could also be declared and implemented as a class feature, the temporary variable could be avoided:
{X [like multi_function_device]}.work (multi_function_device)
If the auxiliary class X is not an option, the current version of the language provides no means to declare an argument as conforming to more than 1 type (e.g., work (d: {PRINTER, SCANNER})), so you would have to resort to preconditions like
work (p: PRINTER)
require
attached {SCANNER} p
do
check
from_precondition: attached {SCANNER} p as s
then
s.scan
end
p.print
end
I think that, if possible, you should use a common ancestor to your multiple types. If you cannot (if you are using library types), you can create descendant classes (MY_PRINTER inherit from PRINTER and DEVICE and MY_SCANNER inherit from SCANNER and DEVICE). Another way is to use ANY as the type, but it is not the best solution.

Initialize a GObject with parameters which are not GObject properties?

I have a GObject "A" which creates an instance of another GObject "B" in its constructor.
The "B" object needs to be passed several construction-only properties. Now when creating an instance of object "A" I want to allow passing values for these properties through the constructor of object "A" on to the constructor of object "B".
The only way I have found to do that was to create identical properties for object "A" and pass their values on to the constructor of "B". These properties would have no further meaning to "A" so this seems like a kludge.
Is there a better way to do what I want?
Have A inherit from B. Then A has all of B's properties automatically.
Don't use properties in A, but instead pass B's properties (or even better, an already-constructed B object) as parameters to A's constructor.
Delay construction of B until A can figure out how it nees to configure B. Add a private flag to A, b_initialized or something, that tells you whether A's internal pointer to B is valid.
Some more clarification on the second suggestion:
A's stuff is constructed in the a_init() function that is provided for by the G_DEFINE_TYPE() macro. But that's not how you get an instance of A. It's usual to write a function, which is part of the public interface of A, like this:
A *a_new()
{
return (A *)g_object_new(TYPE_A, NULL);
}
You can easily extend this to include other parameters:
A *a_new(int b_param_1, int b_param_2)
{
A *a = (A *)g_object_new(TYPE_A, NULL);
a->priv->b = b_new(b_param_1, b_param_2);
return a;
}
This has the disadvantage of leaving your A object in an invalid state (i.e., without a B) if you construct it using g_object_new, for example if you're trying to build it from a GtkBuilder file. If that's a problem, I still strongly suggest refactoring.
Use dependency injection, pass an already initialized object of type B to the constructor of A.
That way the client that is using your class can decide whether to pass in different kinds of Bs (if it makes sense you can even use an interface instead of a class as the B type, writing code against interfaces is generally better than writing code against implementations).
Deriving A from B only makes sense if it really is a specialization of it's parent class.
From the question it isn't clear if derivation makes sense, but it's an often overused method for composition.

composition vs data encapsulation

If a class A is in composition relationship with class B , does it mean that the specific instance of B got via A should only be modifiable through class A not to break the data encapsulation of class A? Or, does composition imply only life-time bind, not the data encapsulation ?
class A
{
B itsB;
B* getB() {return &itsB);
}
void AnotherClass::anyOperation()
{
itsA->getB()->function(); // is this legal ?
}
Composition does not strictly specify the rules concerning whether a composed object should be allowed to be modified outside the composing class or not.
Encapsulation has to do with controlling access to the members defined in a class to outside world. In general ,fields of a class should not be directly accessible by outside code , if this tenet is followed , then the question of allowing it to be modifeid by outside code does not arise. Encapsulation and composition are not related in principle , so in your example , the fact that B is a composed into A itself does not dictate the rule that itsB should not be modifiable outside A.
However , you should think in terms of 1. Who owns the object itsB ? 2. Is it thread-safe to allow it to be modified outside A ? Is it breaking encapsulation ?
If B has public setter functions, the only way in C++ to ensure that nobody retrieves A's B through getB() and changes its value is for getB() to return a constant reference. If it returned a constant pointer, that just ensures that you don't change the pointer; you can still change the values inside the B that the pointer points to.
To mention my problem with different words:
The class A has mB and mC members , where A and B, A and C are in composition relationships.
B and C classes are in association relationship.
What would you say, if I want to link specific instance "mB" to specific instance "mC" (of class A), allowing them to communicate between themselves.
Would it mean to break the encapsulation provided by A for mB and mC ?

Flex/Bison Multi-pass Class Parsing

I am writing a compiler for a toy OO language. I am writing it in C, using Flex and Bison.
Consider the following syntax:
class MyClass {
int m_n;
void MyFunc(int b) {
m_n = 5;
m_p = b;
}
int m_p;
}
My current code will complain that in MyFunc, m_p has not yet been declared (with good reason). So, I came to the conclusion that I need a multi-pass parsing technique - something along the lines of:
1st pass - process variable declarations
2nd pass - process function definitions
First - is this the best way to solve the issue? Are there other methods that I should look into? Second - if this is a favorable solution, would I go about implementing it with a re-entrant lexer/parser?
Thanks
I recently wrote a compiler for an OO language, we had multiple passes (depending on the complexity of the language of course):
Collect all Classes
Build up superclass hierarchy
Collect all methods and fields
Collect variables inside methods etc.
There are reasons why we had to split up the whole process into 4 passes:
You can't build up the superclass hierarchy when not all classes have been processed yet (led to 2. pass)
You can't validate inherited methods (return value, parameters etc.) when the superclass is unknown (led to 2. pass)
You can't process variables when not all fields have been collected yet (led to 4. pass)
You can leave out the second pass if you don't have inheritance in your language of course.
When I look at it now, it should've been possible to merge pass 2 and 3 as all data should be available for pass 3.
The way we implemented it was just by walking through the AST and annotating it with the required symbol tables.

Resources