-
January 30th, 2009, 06:35 AM
#1
[RESOLVED] Visitor pattern with a lot of classes
Hi,
I have a framework with a lot of record types. Each record type inherits from an abstract base record and I use the visitor pattern to ensure type safe handling of derived record types.
Now I encountered a serious compiler bug that prevents me from adding new record types to the visitor interface, so I have to find an alternative.
Any suggestions are highly appreciated.
- Guido
-
January 30th, 2009, 07:30 AM
#2
Re: Visitor pattern with a lot of classes
Just out of curiousity... what is the compiler bug?
-
January 30th, 2009, 07:55 AM
#3
Re: Visitor pattern with a lot of classes
Borland Builder 6, Codegear RAD Studio 2007:
[Fatal linker error] Type index (random number) bad in module xyz.cpp
In my case the linker fails if there are more than 71 virtual methods in an ABC.
- Guido
-
January 30th, 2009, 12:59 PM
#4
Re: Visitor pattern with a lot of classes
Originally Posted by GNiewerth
I have a framework with a lot of record types. Each record type inherits from an abstract base record and I use the visitor pattern to ensure type safe handling of derived record types.
Now I encountered a serious compiler bug that prevents me from adding new record types to the visitor interface, so I have to find an alternative.
Any suggestions are highly appreciated.
Are you using the standard Visitor implementation where all vistor methods in the visitor interface are overloaded (and all called visit)? Maybe the overloading is the problem, not the number of virtual methods? If so you could solve it by giving the visit methods separate names.
-
January 30th, 2009, 01:46 PM
#5
Re: Visitor pattern with a lot of classes
Another option worth trying is different inheritance variations of the Visitor interface. Say Visitor is first split into two, Visitor1 and Visitor2, and then put together again using inheritance, like
class Visitor1 {};
class Visitor2 : public Visitor1 {};
class Visitor : public Visitor2 {};
or
class Visitor1 {};
class Visitor2 {};
class Visitor : public Visitor1, public Visitor2 {};
Also virtual inheritance could be tried in different combinations. Note that the new Visitor class doesn't define any virtual functions of its own, they're all inherited.
There's no particular reason for the above other than trying to induce some other internal memory layout which possibly can be handled by the compiler/linker.
Last edited by _uj; January 31st, 2009 at 12:46 AM.
-
February 2nd, 2009, 04:13 AM
#6
Re: Visitor pattern with a lot of classes
Thank you uj,
that sounds like a viable option since all record types can be divided into groups of records. I think I´ll give it a try.
- Guido
-
February 2nd, 2009, 05:18 AM
#7
Re: Visitor pattern with a lot of classes
I separated my abstract visitor class into two different base classes and let the original visitor class derive both of them. Now I face another compiler error:
[C++ error] main.cpp (line 21): ambiguos element : IVisitor1::visit and IVisitor2::visit
[C++ error] main.cpp (line 25): ambiguos element : IVisitor1::visit and IVisitor2::visit
Code:
#include <iostream>
struct Type1;
struct Type2;
struct IVisitor1 {
virtual ~IVisitor1() {}
virtual void visit( Type1& T ) = 0;
};
struct IVisitor2 {
virtual ~IVisitor2() {}
virtual void visit( Type2& T ) = 0;
};
struct IVisitor : IVisitor1, IVisitor2 {
virtual ~IVisitor() {}
};
struct Type1 {
void accept( IVisitor& V ) { V.visit( *this ); } // ambiguos
};
struct Type2 {
void accept( IVisitor& V ) { V.visit( *this ); } // ambiguos
};
struct ConcreteVisitor : public IVisitor {
void visit( Type1& T ) {
std::cout << "Type 1 visited" << std::endl;
}
void visit( Type2& T ) {
std::cout << "Type 2 visited" << std::endl;
}
};
int main()
{
Type1 t1;
Type2 t2;
ConcreteVisitor cv;
t1.accept( cv );
t2.accept( cv );
}
Since both BCB and Comeau threw the same error messages I assume the code posted above is incorrect and the compiler is right.
I was able to fix this by adding a dynamic_cast, but that seems somewhat dirty to me. Is there any other solution to this?
Fixed:
Code:
struct Type1 {
void accept( IVisitor& V )
{
dynamic_cast<IVisitor1&>( V ).visit( *this );
}
};
struct Type2 {
void accept( IVisitor& V )
{
dynamic_cast<IVisitor2&>( V ).visit( *this );
}
};
Addendum:
Splitting my base visitor class doesn´t solve the orginal problem
Last edited by GNiewerth; February 2nd, 2009 at 05:40 AM.
- Guido
-
February 2nd, 2009, 06:55 AM
#8
Re: Visitor pattern with a lot of classes
Originally Posted by GNiewerth
Splitting my base visitor class doesn´t solve the orginal problem
Sorry to hear that. Note that I didn't suggest a logical split of the Visitor class. The idea was that you would use the Visitor class exactly as before without any changes to the rest of the code.
So again the original Visitor is split into two equal parts, Visitor1 and Visitor2. This split is completely arbitrary with no special logical grouping of the vistit method whatsoever. Then the new Visitor class is built back again but this time it's empty but it inherits the visitor methods from Visitor1 and Visitor2. That's the only change you do. The rest of the pattern should look exactly as it did before.
My hope was that splitting Visitor in the above way would split its vtable into two parts, one half in Visitor1 and the other in Visitor2, and that the compiler/linker would then be able to handle the smaller vtables.
Note that Visitor can be put together using different types of inheritance and you should try different ways.
By the way have you checked out my other suggestion. Here is assumed that the problem is related to the overloading of all the methods in Visitor. Maybe that's what the compiler/linker has trouble with.
Last edited by _uj; February 2nd, 2009 at 07:01 AM.
-
February 2nd, 2009, 07:47 AM
#9
Re: Visitor pattern with a lot of classes
Originally Posted by _uj
Sorry to hear that. Note that I didn't suggest a logical split of the Visitor class. The idea was that you would use the Visitor class exactly as before without any changes to the rest of the code.
So again the original Visitor is split into two equal parts, Visitor1 and Visitor2. This split is completely arbitrary with no special logical grouping of the vistit method whatsoever. Then the new Visitor class is built back again but this time it's empty but it inherits the visitor methods from Visitor1 and Visitor2. That's the only change you do. The rest of the pattern should look exactly as it did before.
My hope was that splitting Visitor in the above way would split its vtable into two parts, one half in Visitor1 and the other in Visitor2, and that the compiler/linker would then be able to handle the smaller vtables.
Note that Visitor can be put together using different types of inheritance and you should try different ways.
By the way have you checked out my other suggestion. Here is assumed that the problem is related to the overloading of all the methods in Visitor. Maybe that's what the compiler/linker has trouble with.
Isn´t that what I did? Instead of declaring one abstract visitor class I declared two base visitor classes IVisitor1 and IVisitor and let the resulting IVisitor class derive from both IVisitor1 and IVisitor2. IVisitor has no virtual functions except the destructor, everything is inherited from its base classes.
- Guido
-
February 3rd, 2009, 03:28 AM
#10
Re: Visitor pattern with a lot of classes
Originally Posted by GNiewerth
Isn´t that what I did? Instead of declaring one abstract visitor class I declared two base visitor classes IVisitor1 and IVisitor and let the resulting IVisitor class derive from both IVisitor1 and IVisitor2. IVisitor has no virtual functions except the destructor, everything is inherited from its base classes.
Well, the problem when distance troubleshooting other people's code is that you can never be sure of what they actually do.
Have you for example tried single inheritance (instead of multiple)? Have you tried to use virtual inheritance? Have you ruled out the possibility that the problem in fact lies with the extensive overloading?
The ambiguity problem is related to overloading in relation to inheritance. This works without the ugly dynamic casts,
Code:
struct IVisitor1 {
virtual ~IVisitor1() {}
virtual void visit1( Type1& T ) = 0; // This class has visit1 methods
};
struct IVisitor2 {
virtual ~IVisitor2() {}
virtual void visit2( Type2& T ) = 0; // this class has visit2 methods
};
struct IVisitor : IVisitor1, IVisitor2 {
virtual ~IVisitor() {}
};
struct Type1 {
void accept( IVisitor& V ) { V.visit1( *this ); } // not ambiguos anymore
};
struct Type2 {
void accept( IVisitor& V ) { V.visit2( *this ); } // not ambiguos anymore
};
This really should do it now. The original Visitor class has been split into two classes each with half the number of overloaded virtual methods. A drawback is that you now have two types of overloaded visit methods, visit1 and visit2 but note that the extensive overloading in the original Visitor pattern is one of convenience only; In principle each "visit" method can be given a unique name without breaking the gist of the pattern.
If it still doesn't work try virtual multiple inheritance (in different combinations), like
Code:
struct IVisitor : virtual IVisitor1, virtual IVisitor2 {
virtual ~IVisitor() {}
};
Also try a single inheritance version like this,
Code:
struct IVisitor1 {
virtual ~IVisitor1() {}
virtual void visit1( Type1& T ) = 0; // This class has visit1 methods
};
struct IVisitor2 : IVisitor1 {
virtual ~IVisitor2() {}
virtual void visit2( Type2& T ) = 0; // this class has visit2 methods
};
struct IVisitor : IVisitor12 { // single inheritance of IVisitor1 and IVisitor2
virtual ~IVisitor() {}
};
-------------------------------------------------------------
Well, I've snowed in a little on a solution based on inheritance. I thought it would have the smallest inpact on the existing code. But now it has become clear that two kinds of visit method, visit1 and visit2, will be necessary and this means changes throughout the code so now that a change is necessary anyway I'll give you an alternative based on composition rather than inheritance. It goes like this,
Code:
struct Type1;
struct Type2;
struct IVisitor1 {
virtual ~IVisitor1() {}
virtual void visit( Type1& T ) = 0;
};
struct IVisitor2 {
virtual ~IVisitor2() {}
virtual void visit( Type2& T ) = 0;
};
struct IVisitor { // composition rather than inheritance
virtual ~IVisitor() {}
virtual IVisitor1& visit1() =0;
virtual IVisitor2& visit2() =0;
};
struct Type1 {
void accept( IVisitor& V ) { V.visit1().visit( *this ); }
};
struct Type2 {
void accept( IVisitor& V ) { V.visit2().visit( *this ); }
};
struct ConcreteVisitor1 : public IVisitor1 {
void visit( Type1& T ) {
std::cout << "Type 1 visited" << std::endl;
}
};
struct ConcreteVisitor2 : public IVisitor2 {
void visit( Type2& T ) {
std::cout << "Type 2 visited" << std::endl;
}
};
struct ConcreteVisitor : IVisitor {
IVisitor1& visit1() {return cv1;}
IVisitor2& visit2() {return cv2;}
private:
ConcreteVisitor1 cv1;
ConcreteVisitor2 cv2;
};
void test() {
Type1 t1;
Type2 t2;
ConcreteVisitor cv;
t1.accept( cv );
t2.accept( cv );
}
Note that in the accept call it will be checked at compiletime that the correct of visit1() and visit2() is called so the integrity of the Visitor pattern still is intact.
I'd say this is the better solution because the Visitor class is definately split into two so the compiler/linker should be able to handle it without problem. But there is the nuisance of having to deal with two ConcreteVisitor classes so this still is an unwanted work-around of course.
Last edited by _uj; February 3rd, 2009 at 05:56 AM.
-
February 3rd, 2009, 04:08 AM
#11
Re: Visitor pattern with a lot of classes
Hello GNiewerth,
Long time no post!
If I may, I'd like to try to explain what I think is the problem (I might be wrong though).
I think the problem is more of an inheritance issue than the design pattern.
Although you split the abstract base class into two,
the inheritance hierarchy is very clear, so I really don't see the need for deriving the elements types virtually.
However, if my understanding doesn't fail me, I think only the immediate derived classes can initialize its base class (derived to base)....um something like that.
I think that means, a virtual member in the base class states that its "immediate" derived class will redefine (optionally) the function, but if it's not done in the derived class, then the version in the base class will be called, which in your case the hierachy line splits into two, thus the ambigous call.
Code:
struct IVisitor : IVisitor1, IVisitor2 {
virtual ~IVisitor() {}
virtual void visit(Type2& T ) = 0;
virtual void visit(Type1& T ) = 0;
};
Thus, by providing a chain-virtual links to the element types of the visitor pattern, the call should be clear.
ah I'm not sure, I tried.
-
February 3rd, 2009, 07:20 AM
#12
Re: Visitor pattern with a lot of classes
Originally Posted by potatoCode
Hello GNiewerth,
Long time no post!
If I may, I'd like to try to explain what I think is the problem (I might be wrong though).
I think the problem is more of an inheritance issue than the design pattern.
Although you split the abstract base class into two,
the inheritance hierarchy is very clear, so I really don't see the need for deriving the elements types virtually.
However, if my understanding doesn't fail me, I think only the immediate derived classes can initialize its base class (derived to base)....um something like that.
I think that means, a virtual member in the base class states that its "immediate" derived class will redefine (optionally) the function, but if it's not done in the derived class, then the version in the base class will be called, which in your case the hierachy line splits into two, thus the ambigous call.
Code:
struct IVisitor : IVisitor1, IVisitor2 {
virtual ~IVisitor() {}
virtual void visit(Type2& T ) = 0;
virtual void visit(Type1& T ) = 0;
};
Thus, by providing a chain-virtual links to the element types of the visitor pattern, the call should be clear.
ah I'm not sure, I tried.
Yes, that fixes the ambiguos method call error, but it makes the splitting pointless. We tried to get rid of large vtables and reintroducing the virtual methods again voids the efforts.
However, I found a solution by replacing the linker.
Thanks to everyone who contributed.
- Guido
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|