I am documenting a C++ project using doxygen. In this project, there are some macros that are correctly documented in the code. In my doxyfile, I specify that I don't want a Files tab this way:
SHOW_FILES = NO
The problem is that when doing this, I have no way of seeing the macro documentation, since it seems to be only accessible through the Files tab:
What I would like is to be able to consult the macro documentation without making file documentation available. Is there a way I could do this?
I browsed the special commands and the configuration options and saw nothing about this. I tried the #copydoc flag, but only the body of the documentation is copied, which is not what I need (I need to move the documentation altogether).
Update : here is one of the macro, with its documentation:
#ifndef NDEBUG
/*********************************************************************************************//**
* #brief Standard assertion MACRO (with message attached)
*
* Asserts that `p_condition` is `true`. If you `#define` the `ABORT_ON_ERROR` flag, this
* MACRO will also abort on a `false` condition. On assertion, a custom message defined by the
* programmer will be printed out along with the assertion information.
*
* #param p_condition
* The condition to assert.
* #param p_message
* The message to print.
*
*************************************************************************************************/
#define ASSERT_MSG(p_condition, p_message) cxinv::HandleAssert(cxinv::AssertLabel::ASSERTION, \
( p_condition ), \
#p_condition, \
__FILE__, \
__FUNCTION__, \
__LINE__, \
p_message \
)
#else
#define ASSERT_MSG(p_condition, p_message) ((void)0)
#endif // NDEBUG
You can put your macros in a group. This will add a modules tab:
/**
* #defgroup macros Add macro group title here
*/
/*********************************************************************************************//**
* #brief Standard assertion MACRO (with message attached)
* #ingroup macros
*
...(cut off, see the rest above)
Related
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.
I intend to include some documented C++ classes (let say AClass) within a Doxygen group (let say GROUP_C), while that group is into another one (let say GROUP_B), and that second group into another, base one (let say GROUP_A). Like this:
/** \addtogroup GROUP_A */
/** #{ */
/** \defgroup GROUP_B */
/** #{ */
/** \defgroup GROUP_C */
/** #{ */
/// Comment
class AClass
{
};
/** #} */
/** #} */
/** #} */
I'm trying to get a clean and logical documentation for that situation, but, as simple as I see it, I have not been able to found anything more specific than the Doxygen official documentation, where nothing is said about any cyclical grouping problems. However, just doxygen-ing the simple code above, such problems occur:
warning: Refusing to add group GROUP_C to group GROUP_B, since the latter is already a subgroup of the former
I also get strange breadcrumbs indications of the generated module under the AClass documentation:
Does anybody know what am I understanding wrong in the nesting-group system of Doxygen?
Thanks in advance!
This solution has been working for me for many years since old versions of Doxygen:
/** \defgroup GROUP_A My top-level group description
*
* Put here a longer description.
*
**/
/** #addtogroup GROUP_B My group B description
* \ingroup GROUP_A
* #{ */
// classes, etc.
/** #} */
/** #addtogroup GROUP_C My group C description
* \ingroup GROUP_B
* #{ */
// classes, etc.
/** #} */
Unfortunately your given example works as expected on doxygen-1.8.8 and on latest master git branch. The warning does not appear.
Is it possible you are including other source files (than the presented example code) into your test run and that other files contain conflicting \defgroup or \addgroup statements that cause the circles in the group structure?
Regarding that "GROUP_A" doubling line below "AClass Class Reference" - I guess that is simply a doxygen bug.
I am trying to add a function to multiple groups but it does not seems to be working.
I followed the instructions from here
Here are the comments I added:
/**
* \defgroup version1 version 1
*/
/**
* \defgroup version2 version 2
*/
/**
* \ingroup version1 version2
*/
BOOL CLoginFormDlg::OnInitDialog(){}
The function only appears in the version2 module and not in the version1 module.
if I write version1 last like this:
/**
* \ingroup version2 version1
*/
BOOL CLoginFormDlg::OnInitDialog(){}
then the function only appears in the version1 module and not the version2 module.
I would like them to appear in both modules
The link you provided includes a paragraph that explains why it doesn't work:
Note that compound entities (like classes, files and namespaces) can be put into multiple groups, but members (like variable, functions, typedefs and enums) can only be a member of one group (this restriction is in place to avoid ambiguous linking targets in case a member is not documented in the context of its class, namespace or file, but only visible as part of a group).
One approach to consider to solve this problem might be to create different Doxygen projects for each version of your code. You would create a Doxyfile for each version.
In this case, instead of trying to include common code in both sets of documentation using the \ingroup command, you would be excluding unrelated code from each set using conditional sections delimited by either \if...\endif or \cond...\endcond.
So in your version1 module, you would do the following:
/**
* \if _VERSION1_
*/
<all code in your version1 module>
/**
* \endif
*/
and in your version2 module:
/**
* \if _VERSION2_
*/
<all code in your version2 module>
/**
* \endif
*/
Code that needs to appear in both modules, such as your BOOL CLoginFormDlg::OnInitDialog(){} function, would have no such conditional commands, so would be included in both sets of documentation.
In your version1 Doxyfile, add the following line:
ENABLED_SECTIONS = _VERSION1_
and in your version2 Doxyfile:
ENABLED_SECTIONS = _VERSION2_
To link your version1 and version2 documentation sets together, you can use Doxygen's tag file mechanism.
See also
\cond
\if
ENABLED_SECTIONS
Tag files
Does NetBeans have C auto comment?
I installed NetBeans IDE 7.2.1 and C/C++ Plugins.
When I type in "/**" and press Enter, it automatically generate some code like bellow.
/**
* #param param1
* #param param2
* #param param3
*/
I'm just wondering if I can modify what it generate.
I want to add more info like author, date, remark.
Simply saying, I want some comment to be generated when I type in "/**" and press Enter like bellow.
(The function is already defined.)
/**
* #author
* #date
* #param param1
* #param param2
* #param param3
* #remark
*/
void do_something( struct sturct_one *param1, int param2, char *param3 )
{
...
}
Please help me.
Jan,
the NetBeans IDE documentation section on adding source code documentation does not mention the possibility to customize the Doxygen template. So a short answer to your question
I'm just wondering if I can modify what it generate
is: no, you can't.
Typically, one does not need any extras, e.g. the tags #author and #date from your example are unnecessary if you use a version control system. Did you considered using a CVS?
As an alternative solution (though not as elegant as typing '/**') you could use code templates. As described in the NetBeans documentation, you can define abbreviations for code templates. In your case you could define templates for Doxygen comments with #author, #date, #remark tags and 1, 2, 3, ... parameters, and use abbreviations 1, 2, ... to quickly insert the comments.
Is it possible to document preprocessor defines in Doxygen? I expected to be able to do it just like a variable or function, however the Doxygen output appears to have "lost" the documentation for the define, and does not contain the define itself either.
I tried the following
/**My Preprocessor Macro.*/
#define TEST_DEFINE(x) (x*x)
and
/**#def TEST_DEFINE
My Preprocessor Macro.
*/
#define TEST_DEFINE(x) (x*x)
I also tried putting them within a group (tried defgroup, addtogroup and ingroup) rather than just at the "file scope" however that had no effect either (although other items in the group were documented as intended).
I looked through the various Doxygen options, but couldn't see anything that would enable (or prevent) the documentation of defines.
Yes, it is possible. The Doxygen documentation says:
To document global objects (functions, typedefs, enum, macros, etc),
you must document the file in which they are defined. In other words,
there must at least be a
/*! \file */
or a
/** #file */
line in this file.
You can use #defgroup, #addtogroup, and #ingroup to put related items into the same module, even if they appear in separate files (see documentation here for details). Here's a minimal example that works for me (using Doxygen 1.6.3):
Doxyfile:
# Empty file.
Test.h:
/** #file */
/**My Preprocessor Macro.*/
#define TEST_DEFINE(x) (x*x)
/**
* #defgroup TEST_GROUP Test Group
*
* #{
*/
/** Test AAA documentation. */
#define TEST_AAA (1)
/** Test BBB documentation. */
#define TEST_BBB (2)
/** Test CCC documentation. */
#define TEST_CCC (3)
/** #} */
Foo.h:
/** #file */
/**
* #addtogroup TEST_GROUP
*
* #{
*/
/** #brief My Class. */
class Foo {
public:
void method();
};
/** #} */
Bar.h:
/** #file */
/**
* #ingroup TEST_GROUP
* My Function.
*/
void Bar();
In this case, the TEST_DEFINE documentation appears in the Test.h entry under the Files tab in the HTML output, and the TEST_AAA etc. definitions appear under Test Group in the Modules tab together with class Foo and function Bar.
One thing to note is that if you put the file name after the #file command, e.g:
/** #file Test.h */
then this must match the actual name of the file. If it doesn't, documentation for items in the file won't be generated.
An alternative solution, if you don't want to add #file commands, is to set EXTRACT_ALL = YES in your Doxyfile.
I hope this helps!
In my "C" files, I use a comment format and #define line like this:
/** #brief Number of milli-seconds to wait*/
#define kTimeoutMSec (2)
My html documents do end up containing documentation I specify. (I do have #file at the top of the file and EXTRACT_ALL=YES)
Try setting EXTRACT_ALL option, I have that set in my project and it generates documentation for #defines. There might be a more elegant way of doing it without using EXTRACT_ALL so be sure to check the documentation
http://www.doxygen.nl/config.html#cfg_extract_all
Adding to the previous answers, it is also needed to have ENABLE_PREPROCESSING=YES on the Doxyfile.