WARNING
=======

XMFC is EXPERIMENTAL and for the KNOWLEDGEABLE programmer. Use it at your
own risks.

The information provided here is minimal. UTSL (Use The Source, Luke) !!!

Three demos can be found in the 'DEMO' directory: one for virtual CObject
bases, one for virtual CWnd bases and one for better exceptions.

Installation
============

XMFC comes as a set of zipped files that replace some of the regular MFC
source files. To install, unzip XMFC.ZIP to your MFC directory with the
-d -o options. You surely want to backup your MFC before doing this, or
unzip to a copy of MFC.

You will have to recompile the MFC variants you need. Support for virtual
inheritance (VI) of CObject is always compiled in.

The makefile now contains a WNDBASE symbol. When set to "V", support for
VI of CWnd will be compiled in. The result is a {MODEL}AFXVW{DEBUG} lib.

Usage notes
===========

Runtime Type Information (RTI)
------------------------------

The IMPLEMENT_DYNAMIC/DYNCREATE/SERIAL scheme has been extended.
Implementing multiple bases goes like this:

class Derived : public Base, public Base2, ..., public Basen
    {
    DECLARE_DYNAMIC/DYNCREATE/SERIAL(Derived)
    };

IMPLEMENT_DYNAMIC/DYNCREATE/SERIAL(Derived, Base1)
IMPLEMENT_BASE(Derived, Base2)
...
IMPLEMENT_BASE(Derived, Basen)

Shortcuts are provided for 2 or 3 bases:

IMPLEMENT_DYNAMIC/DYNCREATE/SERIAL_2(Derived, Base1, Base2)
IMPLEMENT_DYNAMIC/DYNCREATE/SERIAL_3(Derived, Base1, Base2, Base3)

When implementing Serialize methods, don't forget to serialize all the
persistent bases. Helpers are provided for that task; examine the
BEGIN_PERSISTENT/PERSISTENT_MEMBER/END_PERSISTENT family of macros.

Two macros are provided that emulate the new C++ dynamic_cast<> operator.
CAST(pointer, type) casts between the bases of an object, with proper
pointer adjustment. CCAST stands for Const CAST. NULL is returned if the
target base is not a base of the actual class of the object.

Compiling for Windows
---------------------

You must define the preprocessor symbol AFX_VIRTUAL_WND_BASE to enable VI
of CWnd. VI support is then enabled and CFrameWnd, CDialog and CView VI of
CWnd. If you use MSVC to build your makefiles, turn 'Use AFX' off and list
{MODEL}AFXVW{DEBUG} and other relevant libs. Otherwise, MSVC will keep
replacing {MODEL}AFXVW{DEBUG} with {MODEL}AFXW{DEBUG}.

The message map macros have been extended:

class DerivedWnd : public Base1Wnd, public Base2Wnd, public BasenWnd
    {
    public:

    protected:

    private:

    DECLARE_DYNAMIC(DerivedWnd)
    };

// RTI for DerivedWnd, needed

BEGIN_MI_MESSAGE_MAP(DerivedWnd)
    MESSAGE_MAP_BASE(DerivedWnd, Base1Wnd)
    MESSAGE_MAP_BASE(DerivedWnd, Base2Wnd)
    // ...
    MESSAGE_MAP_BASE(DerivedWnd, BasenWnd)
END_MESSAGE_MAP_BASE(DerivedWnd)
    // message handlers
END_MESSAGE_MAP

Once again, helpers are provided:

BEGIN_MESSAGE_MAP_2(DerivedWnd, Base1Wnd, Base2Wnd)
    // message handlers
END_MESSAGE_MAP

BEGIN_MESSAGE_MAP_2(DerivedWnd, Base1Wnd, Base2Wnd, Base3Wnd)
    // message handlers
END_MESSAGE_MAP

IMPORTANT NOTE: since the /vmg (use full generality pointers) is hopelessly
bugged, a heavyweight workaround had to be implemented. It builds on top
of the RTI extensions. As a consequence, the following rules MUST be
respected:

- all CWnd-derived classes must be DECLARED_DYNAMIC or better

- IMPLEMENT_DYNAMIC/DYNCREATE/SERIAL must be in the same file as and come
  before the MESSAGE_MAP implementation

- a class that directly inherits virtually a CWnd-derived class must have
  a message map - even if it inherits only that one base

- a message handler cannot be a virtual function: forget about
  'afx_msg virtual' as long as MS has not fixed /vmg

Better Exceptions
-----------------

Every CObject derivate now checks if it sits on the stack. If yes,
it pushes its address on an 'unwind stack' and has its dtor invoked when
unwinding exception contexts. Yes, there is a runtime penalty, but it is
minimal, temporary (true exceptions soon !) and pays off in terms of
quality of design.

The process is ALMOST transparent. An object that can undergo the unwinding
process and that has CObject-derived data members MUST invoke
CObject::SetUnwindObject() in each of its constructors. If it has none,
the appropriate ctors must be added. Anybody who knows how to do away with
this, please tell me.

Consequently, CString and other classes that were previously not CObject
rooted now are. It's worth it:

    CString foo;

    TRY {
        // ...
        }

    CATCH_ALL
        {
        // foo.Empty() NO MORE !
        }

    END_CATCH_ALL

Better isn't it ?


Have fun. Keep in touch.

Jean-Louis Leroy
