Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OOP vs macro problem

Tags:

c++

oop

I came across this problem via a colleague today. He had a design for a front end system which goes like this:

class LWindow
{
   //Interface for common methods to Windows
};

class LListBox : public LWindow
{
   //Do not override methods in LWindow.
   //Interface for List specific stuff
}

class LComboBox : public LWindow{} //So on

The Window system should work on multiple platforms. Suppose for the moment we target Windows and Linux. For Windows we have an implementation for the interface in LWindow. And we have multiple implementations for all the LListBoxes, LComboBoxes, etc. My reaction was to pass an LWindow*(Implementation object) to the base LWindow class so it can do this:

void LWindow::Move(int x, int y)
{
   p_Impl->Move(x, y);   //Impl is an LWindow*
}

And, do the same thing for implementation of LListBox and so on

The solution originally given was much different. It boiled down to this:

#define WindowsCommonImpl {//Set of overrides for LWindow methods}

class WinListBox : public LListBox
{
    WindowsCommonImpl     //The overrides for methods in LWindow will get pasted here.
    //LListBox overrides
}

//So on

Now, having read all about macros being evil and good design practices, I immediately was against this scheme. After all, it is code duplication in disguise. But I couldn't convince my colleague of that. And I was surprised that that was the case. So, I pose this question to you. What are the possible problems of the latter method? I'd like practical answers please. I need to convince someone who is very practical (and used to doing this sort of stuff. He mentioned that there's lots of macros in MFC!) that this is bad (and myself). Not teach him aesthetics. Further, is there anything wrong with what I proposed? If so, how do I improve it? Thanks.

EDIT: Please give me some reasons so I can feel good about myself supporting oop :(

Going for bounty. Please ask if you need any clarifications. I want to know arguments for and vs OOP against the macro :)

like image 449
nakiya Avatar asked Dec 02 '10 10:12

nakiya


2 Answers

Your colleague is probably thinking of the MFC message map macros; these are used in important-looking places in every MFC derived class, so I can see where your colleague is coming from. However these are not for implementing interfaces, but rather for details with interacting with the rest of the Windows OS.

Specifically, these macros implement part of Windows' message pump system, where "messages" representing requests for MFC classes to do stuff gets directed to the correct handler functions (e.g. mapping the messages to the handlers). If you have access to visual studio, you'll see that these macros wrap the message map entries in a somewhat-complicated array of structs (that the calling OS code knows how to read), and provide functions to access this map.

As MFC users, the macro system makes this look clean to us. But this works mostly because underlying Windows API is well-specified and won't change much, and most of the macro code is generated by the IDE to avoid typos. If you need to implement something that involves messy declarations then macros might make sense, but so far this doesn't seem to be the case.

Practical concerns that your colleague may be interested in:

  • duplicated macro calls. Looks like you're going to need to copy the line "WindowsCommonImpl" into each class declaration - assuming the macro expands to some inline functions. If they're only declarations and the implementations go in a separate macro, you'll need to do this in every .cpp file too - and change the class name passed into the macro every time.
  • longer recompile time. For your solution, if you change something in the LWindow implementation, you probably only need to recompile LWindow.cpp. If you change something in the macro, everything that includes the macro header file needs to be recompiled, which is probably your whole project.
  • harder to debug. If the error has to do with the logic within the macro, the debugger will probably break to the caller, where you don't see the error right away. You may not even think to check the macro definition because you thought you knew exactly what it did.

So basically your LWindow solution is a better solution, to minimize headaches down the road.

like image 186
martin_ljchan Avatar answered Sep 28 '22 07:09

martin_ljchan


Does'nt answer your question directly may be, but can't help from telling you to Read up on the Bridge Design pattern in GOF. It's meant exactly for that.

Decouple an abstraction from its implementation so that the two can vary independently.

From what I can understand, you are already on the right path, other than the MACRO stuff.

My reaction was to pass an LWindow*(Implementation object) to the base LWindow class so it can do this:

like image 31
Chubsdad Avatar answered Sep 28 '22 07:09

Chubsdad