Strange CLI/C++ issue...

Kerion

06-12-2008 20:45:18

So I am working on getting MOGRE 1.6 together, and I just ran in to a really sticky issue that I can't seem to think of an elegant solution for. Basically, in OGRE 1.6, ShadowListener was changed to SceneManager::Listener. Not a big issue, we can generate nested native directors for things like that. The issue is that we generate a non-nested interface for the event sink (in this case SceneManager) to impelement. In most cases, that's fine, but in this case the interface has a method on it that uses an enum that is nested in-side SceneManager (SceneManager::IlluminationRenderStage). So you have an interface defined as such:


interface class ISceneManager_Listener_Receiver
{
void PreFindVisibleObjects( Mogre::SceneManager^ source, Mogre::SceneManager::IlluminationRenderStage irs, Mogre::Viewport^ v );
void PostFindVisibleObjects( Mogre::SceneManager^ source, Mogre::SceneManager::IlluminationRenderStage irs, Mogre::Viewport^ v );
void ShadowTexturesUpdated( size_t numberOfShadowTextures );
void ShadowTextureCasterPreViewProj( Mogre::Light^ light, Mogre::Camera^ camera, size_t iteration );
void ShadowTextureReceiverPreViewProj( Mogre::Light^ light, Mogre::Frustum^ frustum );
bool SortLightsAffectingFrustum( Mogre::LightList^ lightList );
};


That uses an enum defined in SceneManager, which isn't defined in the header yet, because it implements the interface that's relying on an enum nested in the class. Yes, it's as confusing as it sounds.

Forward declaring SceneManager in the header didn't fix the issue, because that interface is attempting to use an implementation detail on the class, which isn't defined yet.

You can't move the interface below SceneManager in the file, because SceneManager implements that interface (and would require the same implementation level details that a forward deceleration wouldn't cover).

So I am at a loss as to how to solve this. One thought is to force the enum to be global (through an Attributes.xml switch of some kind), but that would "break" the API, in that it wouldn't be dead on with OGRE. You wouldn't reference Mogre.SceneManager.IlluminationRenderStage, but simply Mogre.IlluminationRenderStage.

I really can't think of any other possible solutions, but maybe there is some CLI/C++ wizardry I am missing.

Bekas

06-12-2008 22:01:20

Ugh, C++ sucks at handling nested types.

The whole director/receiver scheme probably needs some rethinking.
Do what's quickest, either put the enum in global scope or ignore it out and put some TODO comment somewhere so we can revisit the whole thing.

Kerion

07-12-2008 05:30:07

For the time being, I've set the listener to WrapType Interface, which solves the problem.

The more I think about it, the more I like the idea of all the listeners being interfaces. It would seem to me the native director types cause quite a bit of indirection and overhead in this case. I am a fan of events, but perhaps we should let people write their own event frameworks and simply keep our listeners true to OGRE as interfaces.

Just a thought, in any case, keep it an interface for this listener fixed the issue.

Bekas

07-12-2008 12:40:13

This won't work. After you implement the interface, how is Ogre going to call your methods ? How will the native director class be created ?

Another approach would be to have a managed class that, when instantiated, it creates a native director proxy that calls into the methods of the managed class. You would subclass it and override its methods and the proxy would call them.

Then you have the issue that *all* methods of the native Ogre class get redirected to a managed method, even the ones which the user didn't care about and didn't override. Events handle this by allowing each native method to be "overriden" separately; the managed class where you subscribe for events knows exactly which methods you subscribed to (and "overriden") and notifies the director to only redirect these methods to the managed land.

Another benefit that events have over the "abstract overridable class", is that you can have multiple delegates registered for an event and there will be only one native-> managed switch:

native -> managed -> delegate1 + delegate2 + delegate3...

But with the overridable class there will be context switches for each listener class that you use:

native-> managed -> listener1
native-> managed -> listener2
native-> managed -> listener3
....

Bekas

07-12-2008 12:56:17

Here's an idea that will probably simplify the director/receiver scheme:

A managed version of the Listener class, but with the twist that it just contains events for you to register, eg:


FrameListener flist = new FrameListener();
// register for the frame-started event
flist.FrameStarted += new FrameListener.FrameStartedDelegate(frame_started);
root.AddFrameListener(flist);


This will obviate the need to have each class that has an "addListener" method become a "receiver" where you subscribe for the events (ISceneManager_Listener_Receiver will be removed).

It will also make the C++ -> C# conversion easier. People see a C++ "addListener" call and get confused trying to find the equivalent for Mogre.

What do you think ?

Kerion

07-12-2008 14:47:44

That sounds like a reasonable compromise, and still allows you to leverage the power of delegate multi-casting in managed code.

Kerion

07-12-2008 14:53:47

Though, I have to wonder, if defining a listeners type as Interface doesn't work, why were there listeners already in Attributes.xml defined as Interface? Such as WindowEventListener?

Also, all types wrapped as Interface have a Proxy class that implements making them callable. I assumed this was the wiring to make callbacks on those interfaces work. If that's not the case, then we've had some broken listeners for a while.

Kerion

07-12-2008 17:51:47

As an update, I've gotten a first pass at MOGRE 1.6 to compile, but I can't get it to link. I get a ton of errors like this:


error LNK2020: unresolved token (06000209) Mogre.PlaneList::get_ReadOnlyInstance


It all seems to be related to the STL wrapper code, but I can't figure out why.

Bekas

07-12-2008 17:57:15

Though, I have to wonder, if defining a listeners type as Interface doesn't work, why were there listeners already in Attributes.xml defined as Interface? Such as WindowEventListener?

Also, all types wrapped as Interface have a Proxy class that implements making them callable. I assumed this was the wiring to make callbacks on those interfaces work. If that's not the case, then we've had some broken listeners for a while.

Doh, I have trouble remembering how things actually work :roll:

You are absolutely right, interfaces have a corresponding class that you override in order to implement the interface. There's an elaborate mechanism that involves using reflection in order to figure out which methods the user actually overrides and notifies the director to only call these methods.

This is a better choice than events when you don't subscribe many listeners, e.g. Node::setListener accepts only one listener. When there are add/remove methods it is probably better to have events since they have the multicast benefit.

Bekas

07-12-2008 18:06:25

As an update, I've gotten a first pass at MOGRE 1.6 to compile, but I can't get it to link. I get a ton of errors like this:


error LNK2020: unresolved token (06000209) Mogre.PlaneList::get_ReadOnlyInstance


It all seems to be related to the STL wrapper code, but I can't figure out why.

The implementation is missing, is MogrePlane.cpp linked in ?
There must be a CPP_DECLARE_STLVECTOR macro somewhere compiled in for PlaneList.

Kerion

07-12-2008 18:14:22

As an update, I've gotten a first pass at MOGRE 1.6 to compile, but I can't get it to link. I get a ton of errors like this:


error LNK2020: unresolved token (06000209) Mogre.PlaneList::get_ReadOnlyInstance


It all seems to be related to the STL wrapper code, but I can't figure out why.

The implementation is missing, is MogrePlane.cpp linked in ?
There must be a CPP_DECLARE_STLVECTOR macro somewhere compiled in for PlaneList.


MogrePlane.cpp is linked in, but this happens on more than PlaneList. Basically every STL wrapper in the DLL is causing this kind of error. I am messing with a couple of things now to see if I can fix it.

Kerion

07-12-2008 20:38:48

I figured part of it out. I had accidentally messed up a subtle project change you had made, renaming the output object of auto\MogrePlane.cpp to MogrePlane1.obj.

I've actually renamed custom\MogrePlane.* to custom\MogrePlaneImpl.h to work around the issue so the obscure project setting doesn't have to be maintained.

The other issue was that types defined as WrapType Interface, that had protected member typedefs, were generating the correct STL defines for the header, but not the CPP. This has been corrected.

Bekas

07-12-2008 20:54:24

I figured part of it out. I had accidentally messed up a subtle project change you had made, renaming the output object of auto\MogrePlane.cpp to MogrePlane1.obj.
I didn't do such a change, it was probably Marioko's.

I've actually renamed custom\MogrePlane.* to custom\MogrePlaneImpl.h to work around the issue so the obscure project setting doesn't have to be maintained.

The other issue was that types defined as WrapType Interface, that had protected member typedefs, were generating the correct STL defines for the header, but not the CPP. This has been corrected.

Awesome! :)