# MFC, Splitter windows, and CViews Oh my!

This topic is 1866 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I have a quick question that hopefully someone intimately familiar with the workings of MFC can answer for me.

I'm writing a content creation tool for my engine. The editor for the Windows version is using MFC and splitter windows to replicate the same kind of interface that rendering software usually provides. Problem is, because it's graphics-API-agnostic and the engine is platform independent (assuming one is using a rendering driver written for that platform), it means each renderer has a set of CView classes (Top, Front, Side, Perspective, kinda thing) for each platform supported by the graphics driver (presently D3D9, and OGL). This itself isn't so much of an issue as it is creating an problem I'm not sure how to get around. The crux of issue is, when you use splitter windows, you make a call to CreateView (for dynamic splitters) through the splitter window object. One of the parameters is usually some form of:

(..,RUNTIME_CLASS(<statically/hardset-class-name here with no quotes>),...);



Example:

m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(cSomeClassName), CSize(cr.Width() / 2, cr.Height()), pContext);


It would be a damn shame to have to utilize some brute-force method of if/else or switch/case statements to determine which view class to specify in compile time -- after so much effort has been made to ensure things are as "runtime-flexible" as possible.

Now RUNTIME_CLASS boils down to a macro that basically calls GetRuntimeClass. Ideally, what I would like is to just be able to pass in either a char * or (better yet) an std::string.

Example:

m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS("cSomeClassName"), CSize(cr.Width() / 2, cr.Height()), pContext);


But now we're blurring over the lines of compile-time vs runtime. I've seen some stuff that gets closer to what I'm after, by making the passed-in-parameter <instantiated_object_name>->GetRuntimeClass(), but then it implies that the object is created before the call to CreateView???

Example:

m_wndSplitter.CreateView(0, 0, Some_InstantiatedObject->GetRuntimeClass(), CSize(cr.Width() / 2, cr.Height()), pContext);


Anyone have any experience in dealing with this kind thing? What did you do to get around it? I would bet big money other engine vendors are not using gargantuan if/else or switch/case statements for every possibility -- For one, that would hardly be elegant and two, the maintainability would be hellish. Are they just not using MFC? (period, end of discussion, full-stop?) But then IF that is the case, what ARE they using? I can't picture them writing their own splitter classes that hook into the APIs of the GDI subsystem of Windows, that alone would be pretty miserable. WxWindows? Embacadero's VCL? QT? (I've heard licensing costs on QT is pretty bad). There's gotta be a way of not using a hardset/compile-time classname to pass through CreateView. Thanks in advance!

-- StakFallT

Edited by StakFallT

##### Share on other sites
One way could be to have std::map somewhere that is initialized with the class string of each object and its CRuntimeClass pointer. When you create your views, look up the name, and use the CRuntimeClass pointer you find.

Obviously the map then needs to be initialized somewhere... One way of doing it could be to create a new preprocessor macro that you use instead of the regular IMPLEMENT_DYNCREATE, that does what IMPLEMENT_DYNCREATE does, plus adds the CRuntimeClass to your map by creating an instance of a simple struct/class. The constructor of this simple struct would then register your object in the map. You can solve creation-order uncertainty by having your map accessed by a function, where the map is a static unique_ptr in the function that you initialize on first access.

It's not pretty, but it's a common trick to implement an object factory with self-registering objects. The alternative is, as you already figured out, to use long chains of if-statements.

##### Share on other sites

Sorry for the late reply, been mulling over your reply. It sounds like that's almost identical to how I'm achieving multi-rendering-driver capability (I.e. self-registering and using a map with factory functions, etc.). My initial idea was to do just that (just like you came up with), but what I couldn't figure out is how to make it work so that was is passed in gets translated to an identifier and doesn't stay a string. But I think if the GetThisClass (I made the same error a few times in this thread -- the RUNTIME_CLASS macro boils down to a GetThisClass call NOT GetRuntimeClass) function is set to static I should be able to nix the RUNTIME_CLASS macro that takes an identifier and instead basically make the call to CreateView like this:

//Some_Map["D3D9_Top"]'s value is basically a function ptr to Direct3D9_TopView::GetThisClass()
m_wndSplitter.CreateView(0, 0, Some_Map["D3D9_Top"], CSize(cr.Width() / 2, cr.Height()), pContext);


and if the GetThisClass()  is set to static, which I believe it is (I'll have to check the afx headers), then it should work correctly; is that how you see it?

-- StakFallT

Edited by StakFallT

##### Share on other sites
Yes, unless I remember this completely wrong, and I think I don't, the GetThisClass / RUNTIME_CLASS just returns a pointer to the global instance of a class' CRuntimeClass.

##### Share on other sites

Awesome, thanks much!

(Marking this as solved)

EDIT 1: hmm... at least I thought I could change the topic to append the word SOLVED on the end... guess not... coulda sworn I've seen that ability in the past though...

Edited by StakFallT

##### Share on other sites

Are they just not using MFC?

Just for the record, no-one is using MFC unless they absolutely have (i.e. working with a legacy code base).

MFC is just awful.

If you want a cross-platform UI, look at Qt. If you're happy on windows, just use winforms.

##### Share on other sites

Well what are the major engine vendors (Unity, Epic, id Software, Unigine, etc.) using? Are they really all using QT? I know Unity's editor is cross platform, and Epic at one point, I thought anyhow, had a Linux editor accompanied with Unreal Tournament, not sure about id Software, or Unigine. The thought of them licensing something instead of hand making it seems... peculiar (to me anyhow) -- just because so much of their philosophy is "[they] can do it better". (Well that and I remember hearing somewhere that QT's license is fairly expensive). I'm not asking merely to just copy the same philosophy for the sake of copying some arbitrary philosophy, but I ask because I'm trying to figure out if it's a battle-tested design by companies that stand to loose millions. I do definitely understand the benefits, a UI that works crossplatform etc. would certainly be fantastic. But it brings me back to the question, is there any major engine vendors (that are NOT open source) trusting their engine's editor to it? Has it been battle tested by them? What about the major commercial rendering programs (Maya, 3DSMax, Solidworks -- exclude open source again)? I say exclude open source because the programs I mentioned are made by companies who stand to lose BIG money if they have to go back and revisit something that broke their current codebase. If they trust their products to it, then I'd say that speaks volumes.

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

(You must login to your GameDev.net account.)

• 9
• 13
• 9
• 9
• 15
• ### Forum Statistics

• Total Topics
634071
• Total Posts
3015340
×