Refactor MFC message maps to include fully qualified member function pointers - c++

I have a codebase where MFC message maps are written in this form:
BEGIN_MESSAGE_MAP(SomeForm, BaseForm)
ON_COMMAND(CID_ButtonAction, OnButtonAction)
END_MESSAGE_MAP()
This compiles just fine in MSVC. When I want to compile the same code in Clang I get a call to non-static member function without an object argument error because OnButtonAction is not a correct form for specifying a member function pointer. The code can be easily fixed:
ON_COMMAND(CID_ButtonAction, &SomeForm::OnButtonAction)
or we can use ThisClass typedef from the BEGIN_MESSAGE_MAP() macro:
ON_COMMAND(CID_ButtonAction, &ThisClass::OnButtonAction)
So far so good...the only problem is that I have hundreds of these message map entries in a lot of separate files. Is there any tool that can fix this? Some obscure Visual Studio magic? Or is it possible to use replacement via regex here?

In the end I came up with a sed command that I ran from MinGW:
sed -b -i -re '/^BEGIN_MESSAGE_MAP/,/^END_MESSAGE_MAP/{/(BEGIN_MESSAGE_MAP|\/\/)/!s/(.*),\s{0,}/\1, \&ThisClass::/;}' *.cpp
To explain what it does:
-b treat files as binary (optional, to keep line endings in Windows)*
-re support extended regular expressions
-i in-place substitution
/^BEGIN_MESSAGE_MAP/,/^END_MESSAGE_MAP/ match only text between these two strings
/!s substitution command that will ignore whatever you match before it
/\(BEGIN_MESSAGE_MAP\|\/\/\)/ matches line beginnings to ignore (either the first line of the message map or commented-out lines)
/(.*),\s{0,}/\1, \&ThisClass::/ substitutes the last comma on a line followed by 0+ whitespaces with , &ThisClass::
Sample input:
BEGIN_MESSAGE_MAP(SomeForm, BaseForm)
ON_COMMAND(CID_ButtonAction, OnButtonAction)
ON_NOTIFY_EX(CID_Notify, 0, OnNotify)
END_MESSAGE_MAP()
Output:
BEGIN_MESSAGE_MAP(SomeForm, BaseForm)
ON_COMMAND(CID_ButtonAction, &ThisClass::OnButtonAction)
ON_NOTIFY_EX(CID_Notify, 0, &ThisClass::OnNotify)
END_MESSAGE_MAP()
This worked nicely, for ~500 files I only had to make two manual adjustments where the class method membership notation was already used. The sed command could be adjusted to account for this (e.g. check if the last comma on the line was followed by &) but this was just good enough for my purposes.
EDIT - added -b option. This treats the files as binary. On Windows this prevents replacing original newline characters by Unix ones - without this option enabled the git diff for any processed file will look like the whole file has been deleted and added again.

The error message is a bit strange and I suppose it has to do with a difference between Visual Studio and CLANG so far as processing the source.
The compiler I have handy is Visual Studio 2005 and I have an MFC application I am working on so the MFC source for Visual Studio 2005 is handy. I took a quick look at Visual Studio 2015 with the same solution and it appears the MFC header files are similar. So I am going to base this on Visual Studio 2005 MFC.
The ON_COMMAND() macro located in afxmsg_.h is defined as the following:
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
static_cast<AFX_PMSG> (memberFxn) },
// ON_COMMAND(id, OnBar) is the same as
// ON_CONTROL(0, id, OnBar) or ON_BN_CLICKED(0, id, OnBar)
And AFX_PMSG is defined in the file afxwin.h as:
// pointer to afx_msg member function
#ifndef AFX_MSG_CALL
#define AFX_MSG_CALL
#endif
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
The class CCmdTarget is a base class from which is derived other classes such as CWnd and CWinThreadand other MFC classes that use a message map.
So the ON_COMMAND() macro is using static_cast<> to what should be a base class of the window or thread target. Perhaps someone else, more knowledgable can provide an actual explanation as to what the compiler is doing and how the C++ language specification would treat this construct.
However on a more practical note, what I suggest is that you write your own version of the ON_COMMAND() macro and insert this version in the StdAfx.h file that is in each project of your solution. I picked the StdAfx.h file since there is only one per project and it is a central point where a single modification can affect multiple compile units.
At the bottom of the file after all the various includes and before the #endif that closes the test for the header file already included, add the following lines of source.
#undef ON_COMMAND
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
static_cast<AFX_PMSG> (&ThisClass :: memberFxn) },
// ON_COMMAND(id, OnBar) is the same as
// ON_CONTROL(0, id, OnBar) or ON_BN_CLICKED(0, id, OnBar)
This does two things.
First of all it undefines the current definition of the ON_COMMAND() macro so that you can replace it with your own.
Secondly it uses the class method membership notation for the method pointer. I am unable to test with CLANG however it should do the same source text substitution as you did by hand which you say works.
ON_COMMAND(CID_ButtonAction, &SomeForm::OnButtonAction)
ThisClass is a typedef for the class specified in the BEGIN_MESSAGE_MAP() directive (e.g. BEGIN_MESSAGE_MAP(CFrameworkWnd, CWin)) and is generated by the BEGIN_MESSAGE_MAP() macro which looks like:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
PTM_WARNING_DISABLE \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return GetThisMessageMap(); } \
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
{ \
typedef theClass ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
I tested this approach with Visual Studio and everything compiles just fine and it works with Visual Studio 2005.
Please note there may be other message map macros which may require a similar workaround as the use of the static_cast<AFX_PMSG> seems to be pretty common in most of the message map macros.
A curious difference
Looking into this, one curious difference in the various macros in afxmsg_.h is an entire set of macros that use the class method pointer notation. An example is the following:
#define ON_WM_PAINT() \
{ WM_PAINT, 0, 0, 0, AfxSig_vv, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnPaint)) },
Looking at some of the specific event macros, it appears that they reuse the ON_CONTROL() macro so replacing that macro in addition to the ON_COMMAND() macro would ripple down through the set of control specific MFC macros.
// Combo Box Notification Codes
#define ON_CBN_ERRSPACE(id, memberFxn) \
ON_CONTROL(CBN_ERRSPACE, id, memberFxn)
A summation
Using this approach of overriding the default macros with your own version, it appears that the include file afxmsg_.h contains a list of the what would need to change. It also appears that there are two sets of MFC macros that would need a replacement version, ones near the top of the file (beginning with ON_COMMAND()) and a few macros near the bottom of the include file afxmsg_.h.
For instance the ON_MESSAGE() macro would need a change to:
// for Windows messages
#define ON_MESSAGE(message, memberFxn) \
{ message, 0, 0, 0, AfxSig_lwl, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \
(&ThisClass :: memberFxn)) },
I wonder why there is a mix of styles (possibly due to different people adding new macros over the years and not bothering to change the existing ones?). I am curious why this has not been addressed sometime during the last two decades as MFC dates from at least Visual Studio 6.x and there would have been opportunities to make the macros uniform. For instance the release of Visual Studio 2005 would have been a good time. Perhaps there was a concern for backwards compatibility with that huge Visual Studio 6.x MFC code base?
And now I know why the tailored, specific static_cast<>. It allows for the detection of a class method with the wrong or non-matching interface signature with a compilation error. So the C-style cast is to make things right with the definition for the function pointer in AFX_MSGMAP_ENTRY and the static_cast<> is to catch programmer errors due to a defective interface by issuing a compiler error if the method interface differs from what is expected.

Related

Why is a "user breakpoint" called when I run my project with imported .lib, not when code is inline?

The Situation
I am writing a wrapper library for GPIB communications for setting up specific instruments according to a clients specifications to automate some of their processes. I have to use C++ and the old '98 compiler in VC++ 6.0 running on a Windows NT machine to maintain compatibility with some other devices they use regularly.
I am trying to make a class that combines some GPIB commands into easier to remember functions, while also keeping the capability of directly communicating with the instruments. To that end, I have compiled different parts of my project into different libs and dlls, each dll being a different device that they might want to communicate with. I also made a generic dll base class from which all the specific instrument classes inherit, hopefully making the whole setup as modular as possible.
The Problem
So, with all that said, I have an issue when I try to test the different dlls/modules. I created a project to test the generic base class, added the .lib file to the project, which links to the .dll, and also #included the header file for that dll. testGeneric.cpp looks like this:
#include "GENERIC.h"
void main(void) {
GPIBInstrument hp(2); //connects to device at primary address #2
hp.write("*IDN?");
}
Super simple. To be clear, I also have the GENERIC.lib linked in the "Resource Files" folder in VC++ 6.0, and I have GENERIC.dll accessible from the path variable.
Moving on, GENERIC.h looks like this (select parts):
#ifndef GENERIC_H
#define GENERIC_H
#include <string>
#include <windows.h>
#include "decl-32.h"
#ifdef GENERIC_EXPORT
#define GENERIC_API __declspec(dllexport)
#else
#define GENERIC_API __declspec(dllimport)
#endif
...(Inline exception classes)...
class GENERIC_API GPIBInstrument {
...
public:
void write(std::string command);
...
};
#endif
Just showing the relevant methods. Then GENERIC.cpp:
#define GENERIC_EXPORT
#include "GENERIC.h"
...
void GPIBInstrument::write(std::string command) {
ibwrt (handle, &command[0u], command.length());
std::cout << command << std::endl;
if (ibsta & TIMO) {
timeoutError();
}
if (ibsta & ERR) {
error("Unable to write command to instrument: " + command);
}
}
So, looks pretty good right? No issues. Compiles fine. I try running it, and BLAM! I get this: "User breakpoint called from code at 0x77f7645c". So, then I thought, well maybe it would work if I put all the code from GENERIC.h and GENERIC.cpp into one file, and #included that file all as inline code. So I tried it, and it and it compiled nicely, and ran fine.
Question (<-AHA!... But...)
What am I doing wrong!? Something with the way I'm making the .dll? Or the .lib? Or something else entirely?
EDIT (WHY!?)
So, after a bit of debugging, I found that it was something to do with passing a string literal. So I just modified it to:
std::string command = "*IDN?";
hp.write(command);
and it worked fine. My followup question, is why? What's the difference between having a string literal passed, versus assigning that to a variable and then passing it in?
Using complex types such as std::string as a parameter at a DLL boundary is tricky. You must ensure that the exe and the DLL use the exact same instance of the library code. This requires that you build them both to use the same version of the DLL version of the runtime library.

Display runtime error message on specific line in Xcode

I would like to display custom error message during runtime in Xcode in C/C++ code. This could be used for example for custom assert() function.
I would like to display message inline, like SenTestingKit does:
Right now the only thing I can do is use __builtin_trap to stop at the correct line. But no custom error message is displayed.
I'm not familiar with xcode but here's how it's done almost everywhere else.
Your custom assert will look like this:
#define MY_ASSERT(a1, a2, desc, ...) {\
PrintAssertMessage(...); \
DebugBreak(); \
}
In windows there's already a DebugBreak function in win32 API.
In Linux and most other systems that run IA32/X64 you can simply call int 3 which is the breakpoint trap.
asm ("int 3");
I read that in xcode it's:
__asm {int 3}
or
__asm__("int $3")
or
__asm__("trap")
In any case this should be surrounded by a macro that disables the assert in debug builds as well macro that defines how to cause a break point.

Word addin in C++, IDTExtensibility2, how to write handlers for events? (generic COM help wanted too)

I'm lurking through the very few samples of native Word addins available, trying to find the way to reconstruct VSTO's Document.OnBeforeClose event.
Currently, I figured out that IDTExtensibility2 has a reference to DTE, and that DTE is a same COM-based approach used for Visual Studio extensions.. (so if I'll find a working C++-written VS add-in with same kind of hooks, it will solve my problems fast.)
This was taken from TestAddin2 sample from 2000's:
BEGIN_COM_MAP(CConnect)
COM_INTERFACE_ENTRY2(IDispatch, IRibbonCallback)
COM_INTERFACE_ENTRY(IConnect)
COM_INTERFACE_ENTRY(_IDTExtensibility2)
COM_INTERFACE_ENTRY(_FormRegionStartup)
COM_INTERFACE_ENTRY(IRibbonExtensibility)
COM_INTERFACE_ENTRY(IRibbonCallback)
END_COM_MAP()
So currently I wonder:
how to acquire a proper ENTRY* parameters for capturing document-level events from within Word?
what interface(s) should be implemented to support that?
how to properly implement callback functions (ones that are passed to com object as 'handlers' in VSTO) in pure C++ COM?
is there anything to generate headers from existing COM object, like VS does when showing COM object fields/props?
It seems that at least someone on SO managed to write a native-code addin (packaging a COM addin for deployment), so I'm really expecting help here.
Here is a sample illustrating how to implement event handlers for COM dispinterfaces with ATL's IDispEventImpl/BEGIN_SINK_MAP/SINK_ENTRY_EX/END_SINK_MAP:
http://support.microsoft.com/kb/194179.
To generate COM definitions for MS Word and Office Object Model, you'd need the following files:
MSADDNDR.TLB
MSO.DLL
MSWORD.OLB
VBE6EXT.OLB
You should be able to find them somewhere under "C:\Program Files (x86)\Microsoft Office\". The following import code worked for me a while ago with Office 2007, you may need to tweak it for more recent Office versions:
#import "TypeLib\MSADDNDR.TLB" \
raw_interfaces_only \
no_namespace \
auto_search
#import "TypeLib\MSWORD.OLB" \
raw_interfaces_only \
rename("ExitWindows","MsoExitWindows") \
rename("FindText","MsoFindText") \
rename("DocumentProperties", "MsoDocumentProperties") \
rename("RGB", "MsoRGB") \
auto_search \
exclude("IAccessible", "AddIn", "Adjustments")

Deriving custom archive classes from boost::archive::text_oarchive_impl and boost::archive::text_iarchive_impl

Note:
Boost's archive scheme is based on symmetrical input and output archive classes. It's tedious to write about both of them all the time, so I'll use ?archive to mean both oarchive and iarchive.
Summary:
After changing the base classes of my custom archives from binary_?archive_impl to text_?archive_impl, my custom archive classes are no longer "found" when the compiler is instantiating the serialize(...) methods in my other classes.
Background:
My application was successfully reading and writing files to disk using subclasses of binary_?archive_impl (the documentation and/or code comments recommend this over deriving from binary_?archive). I needed to switch from a binary file format to a text format, so I switched the base classes of my custom archives to text_?archive_impl. That's when everything blew up.
The problem:
My custom archive classes add functionality, including some additional methods which do not exist in their Boost base classes; these methods are called in the serialize(...) methods in many of my classes, and they were working fine. After changing the base classes from binary_?archive_impl to text_?archive_impl, I received compilation errors all over the place complaining that my custom methods do not exist in text_?archive. Well, that's obvious (!!!), but they do exist in my custom archives, and they were working just fine when I was using Boost's binary base classes. What's the deal?
What I found, and my temporary - but undesirable - solution:
After tearing my hair out and going around in circles for about a day, this is what I found...
1) Some time ago (Boost 1.34 I believe), the files "binary_?archive.hpp" were split up into "binary_?archive_impl.hpp" and "binary_?archive.hpp" (the latter #include the former). This was not done to "text_?archive.hpp". (As a result, I changed my application's #include lines from "binary_?archive_impl.hpp" to simply "text_?archive.hpp".)
2) If I split up "text_?archive.hpp" into two parts and #include only the "..._impl.hpp" headers, everything works. (But I really don't want to modify my Boost installation!)
3) Looking more closely at these headers and fiddling around a bit, I found that if I use the original, unmodified headers and comment out the line
BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::text_oarchive)
(and likewise for text_iarchive), then everything works fine again. (By the way I have similar lines in my own archive code to "register" my custom archives.)
The mystery, and my dilemma:
A) Why does the presence of those lines foul up the works? ...and why does removing them make things work? ...and what might I have broken (without knowing it) by doing so?
B) Why were the "text_?archive.hpp" files not split up along with the "binary_?archive.hpp" files a long time ago? (Is the library broken? Should it be fixed?)
C) Is there any way to solve this in my application code without modifying my Boost installation?
P.S. I'm using Boost 1.48 and Visual Studio 2010 (64-bit)
P.P.S. I imagine all of the above would apply equally to text_w?archive
As best I can tell its a bug in boost serialize. We'll see here.
A)
1. Adding BOOST_SERIALIZATION_REGISTER_ARCHIVE with you new archive does not work because the default text archives have already been registered - only on registration seems to be allowed.
2. Removing them makes it work because only your custom classes are registered.
3. By removing them you've broken the ability to use the default text archive - your classes will be registered.
B)
I'm fairly certain that the "text_?archive.hpp" files should have been split up like the "binary_?archive.hpp" files are. Patch boost anyone?
C)
The best solution is to submit a patch to boost that splits the files up. For a temporary solution probably the best way is to put the patched files locally in your project until the patch makes it into boost.
I want this to be a comment since it is a hint rather than an answer. However I am not seeing the option to add a comment to your question (and I do not think edit button will do what I want to).
On my 1.49.0 install I see corresponding implementation files for the text type also. They are in the .ipp format in the impl directory. The time-stamp suggests that they have not been changed recently so should be the same as the 1.48. It might help you unravel the issue.
According to Dave Abrahams, .ipp files are supposed to hide the implementation. Not sure why they chose different styles.
----------+ 1 stackoverflow None 2936 Dec 5 2009 ./binary_iarchive_impl.hpp
----------+ 1 stackoverflow None 2966 Dec 5 2009 ./binary_oarchive_impl.hpp
----------+ 1 stackoverflow None 1392 Nov 25 2007 ./detail/basic_archive_impl.hpp
----------+ 1 stackoverflow None 3458 May 20 2009 ./impl/text_iarchive_impl.ipp
----------+ 1 stackoverflow None 3290 Jul 2 2005 ./impl/text_oarchive_impl.ipp
----------+ 1 stackoverflow None 3020 Jun 26 2008 ./impl/text_wiarchive_impl.ipp
----------+ 1 stackoverflow None 2244 Jul 2 2005 ./impl/text_woarchive_impl.ipp
I had the same problem tying to implement custom archive for my library.
I've found a possible solution trick, it seems to works well so I'll share with you:
There is no way to export a class with a modified serialization syntax in boost archive, so we have to avoid it at all.
boost archive registration uses a properly overloaded function to make a pointer serialization type instance ( as in boost/archive/detail/register_archive.hpp )
# define BOOST_SERIALIZATION_REGISTER_ARCHIVE(Archive) \
namespace boost { namespace archive { namespace detail { \
\
template <class Serializable> \
BOOST_DEDUCED_TYPENAME _ptr_serialization_support<Archive, Serializable>::type \
instantiate_ptr_serialization( Serializable*, Archive*, adl_tag ); \
\
}}}
Note that adl_tag adds a cool overload feature that can be used to make boost able to look inside our implementation. Simply put a new registration declaration as this:
// ARCHIVES REGISTRATION //
namespace MyLib {
struct adl_tag {};
}
namespace boost { namespace archive { namespace detail {
template <class Serializable>
void instantiate_ptr_serialization(Serializable*, int, MyLib::adl_tag ) {}
} } }
# define MYLIB_SERIALIZATION_REGISTER_ARCHIVE(_Archive) \
namespace boost { namespace archive { namespace detail { \
template <class Serializable> \
BOOST_DEDUCED_TYPENAME _ptr_serialization_support<_Archive, Serializable>::type \
instantiate_ptr_serialization( Serializable*, _Archive*, MyLib::adl_tag ); }}}
Now you have to make your own EXPORT macro as in (/boost/serialization/export.hpp):
namespace MyLib {
namespace extra_detail {
template<class T>
struct guid_initializer
{
void export_guid(mpl::false_) const {
// generates the statically-initialized objects whose constructors
// register the information allowing serialization of T objects
// through pointers to their base classes.
boost::archive::detail::
instantiate_ptr_serialization((T*)0, 0,
MyLib::adl_tag());
}
void export_guid(mpl::true_) const {
}
guid_initializer const & export_guid() const {
BOOST_STATIC_WARNING(boost::is_polymorphic< T >::value);
// note: exporting an abstract base class will have no effect
// and cannot be used to instantitiate serialization code
// (one might be using this in a DLL to instantiate code)
//BOOST_STATIC_WARNING(! boost::serialization::is_abstract< T >::value);
export_guid(boost::serialization::is_abstract< T >());
return *this;
}
};
template<typename T>
struct init_guid;
} // extra_detail
} // namespace MyLib
#define MYLIB_CLASS_EXPORT_IMPLEMENT(T) \
namespace MyLib { \
namespace extra_detail { \
template<> \
struct init_guid< T > { \
static guid_initializer< T > const & g; \
}; \
guid_initializer< T > const & init_guid< T >::g = \
::boost::serialization::singleton< \
guid_initializer< T > \
>::get_mutable_instance().export_guid(); \
}} \
/**/
Ok it's all, now you can define your custom archive and register it with:
MYLIB_SERIALIZATION_REGISTER_ARCHIVE(MyLib::xml_iarchive)
and anytime you define a serialization for your class that has a particular syntax only readable by MyLib::custom_archive you can use your export implementation
BOOST_CLASS_EXPORT_KEY(MyClass) // in header
MYLIB_CLASS_EXPORT_IMPLEMENT(MyClass) // in cpp
(Note that exporting of Key remains the same of boost ... )
This is really cool because lets your custom archives and boost archives live together without errors.. Anytime you wants a boost serialization simply use BOOST_CLASS_EXPORT, and anytime you have your class to be serialized use MYLIB_CLASS_EXPORT.
Hope that this could be useful !
Andrea Rigoni Garola

Replacing a name of a function using define macros in cpp correctly

I'm using Eclipse + Qualcomm libraries (in cpp) + Android SDK on Ubuntu. My application runs fine. If I change some code in the qualcomm libraries, it compiles and works correctly.
The problem is: I have changed the name of the project, and I have to change some code in cpp (The name of the function), if I don't do it, I get a Java.lang.UNSATISFIEDLINKERROR.
That's because all the functions have the name as the Android package like this:
Java_org_myproject_marker_MainActivity_onQCARInitializedNative(JNIEnv *, jobject)
Then I define a macro like this:
#define MAIN_ACTIVITY_PREFIX org_myproject_marker_MainActivity
#define VISUALIZER_PREFIX org_myproject_marker_Visualizer
And I change all the correct functions by:
Java_MAIN_ACTIVITY_PREFIX_onQCARInitializedNative(JNIEnv *, jobject)
but I am still getting the Java.lang.UNSATISFIEDLINKERROR exception.
It works if I do it without the #define macro (and write all the lines), but I want to save the cpp code with a top define that changes everything automatically if I need to use it in other projects.
I have read this tutorial. Can't I replace a text inside another text or something like that?
you are looking for string concatenation, like this:
#define MAIN_ACTIVITY_PREFIX(n) Java_org_myproject_marker_MainActivity##n
and then use it like this:
MAIN_ACTIVITY_PREFIX(_onQCARInitializedNative)(JNIEnv *, jobject)
Indeed, a CPP macro wont be expanded in the middle of an identifier. Try with
#define MAIN_ACTIVITY_PREFIX(func) Java_org_myproject_marker_MainActivity##func
That gives you a macro that will prepend Java_org_myproject_marker_MainActivity to the function name you pass it. Use it as:
MAIN_ACTIVITY_PREFIX(_onQCARInitializedNative)(JNIEnv *, jobject) {
...
}