Apache module command parser prototype - c++

I am creating an Apache2 module and experiencing a weird compilation problem.
This is prototype of my function used to parse config command named "analytics_ip":
static const char *apr_cfg_set_analytics_ip(cmd_parms *cmd, void *config, const char *data);
This is array of command_rec structures containing pointers to this function:
static const command_rec apr_cmds[] =
{
AP_INIT_TAKE1("analytics_ip", apr_cfg_set_analytics_ip, NULL, OR_ALL, ""),
{ NULL }
};
Structure command_rec is declared in header file http_config.h
typedef struct command_struct command_rec;
struct command_struct {
/** Name of this command */
const char *name;
/** The function to be called when this directive is parsed */
cmd_func func;
/** Extra data, for functions which implement multiple commands... */
void *cmd_data;
/** What overrides need to be allowed to enable this command. */
int req_override;
/** What the command expects as arguments */
enum cmd_how args_how;
/** 'usage' message, in case of syntax errors */
const char *errmsg;
};
When I follow cmd_func, it gets to the following declaration:
typedef const char *(*cmd_func) ();
If I am not mistaken, this means "pointer to function returning pointer to char and not accepting any arguments". How can this be possible? Command parsing function has to accept at least one parameter containing a module value of config variable corresponding to that function.
I am using g++ to compile this module.
Error message:
mod_xxx.h:65:2: error: invalid conversion from ‘const char* (*)(cmd_parms*, void*, const char*) {aka const char* (*)(cmd_parms_struct*, void*, const char*)}’ to ‘cmd_func {aka const char* (*)()}’ [-fpermissive]
};
Thanks in advance

cmd_func is a union, it is defined in http_config.h as follows:
typedef union {
/** function to call for a no-args */
const char *(*no_args) (cmd_parms *parms, void *mconfig);
/** function to call for a raw-args */
const char *(*raw_args) (cmd_parms *parms, void *mconfig,
const char *args);
/** function to call for a argv/argc */
const char *(*take_argv) (cmd_parms *parms, void *mconfig,
int argc, char *const argv[]);
/** function to call for a take1 */
const char *(*take1) (cmd_parms *parms, void *mconfig, const char *w);
/** function to call for a take2 */
const char *(*take2) (cmd_parms *parms, void *mconfig, const char *w,
const char *w2);
/** function to call for a take3 */
const char *(*take3) (cmd_parms *parms, void *mconfig, const char *w,
const char *w2, const char *w3);
/** function to call for a flag */
const char *(*flag) (cmd_parms *parms, void *mconfig, int on);
} cmd_func;
enum cmd_how args_how; is responsible for choosing the correct version of the function.
The switch handling it is located in server/config.c (in the invoke_cmd function).
You seem to be using the "take1" version which corresponds to cmd->AP_TAKE1 or simply cmd->take1.
The problem might be that C and C++ have differences regarding the union initialization. (AP_INIT_TAKE1 uses the { .take1=func } syntax which doesn't work in C++).
You'll have to initialize static const command_rec apr_cmds in a C++-compatible way or move it to a separate object file compiled with C. Or if you're not using C++ then simply compile with gcc.

For the project I'm working on we ended up adding a cast to allow the compilation to complete successfully, and the code seems to work OK as it correctly reads in the values specified in the configuration file. Here's the extract of this practice:
extern "C" {
static const command_rec kiwix_settings[] =
{
AP_INIT_TAKE1("zimFile", (const char* (*)())kiwix_set_zimfilename, NULL, RSRC_CONF, "The ZIM filename in full including the extension"),
AP_INIT_TAKE1("zimPath", (const char* (*)())kiwix_set_path, NULL, RSRC_CONF, "The path to the ZIM file, including the trailing //"),
{ NULL }
};
}
The full file (and indeed the project) are opensourced. Here's the link to the full file https://github.com/kiwix/kiwix-apache/blob/master/mod_kiwix.cpp
PS: thanks for your question and https://stackoverflow.com/users/257568/artemgr's answer as they helped me and another volunteer to work out how to resolve the problem for our project.

Related

cppcheck format string on member functions of C++

aa.h
#ifndef __US_LOG_FILEA_H_
#define __US_LOG_FILEA_H_
namespace AA{
class A{
public:
A();
~A();
static A& Ins(){
static A obj;
return obj;
}
void do_p(const char *cat, int level, const char *Format ...); // ok
void do_p(const char *cat, int level, const char *Format, ...); // error
};
} // namespace AA
extern AA::A g_A;
#endif // __US_LOG_FILEA_H_
formatstr.cpp
void test()
{
g_A.do_p("global func", 2, "%s\n", str);
}
a.cfg:
<?xml version="1.0"?>
<def>
<function name="AA::A::do_p">
<noreturn>false</noreturn>
<leak-ignore/>
<formatstr type="printf"/>
<arg nr="3">
<formatstr/>
<not-uninit/>
</arg>
</function>
</def>
cppcheck --enbale-style --library=a.cfg formatstr.cpp
if void do_p(const char *cat, int level, const char *Format ...); cppcheck output:
warning: %s in format string (no. 1) requires 'char *' bu
t the argument type is 'std::string'. [invalidPrintfArgType_s]
g_A.do_p("global func", 2, "%s\n", str);
but do_p(const char *cat, int level, const char *Format,...); cppcheck output nothing
WHY?
The Cppcheck's version is 1.89.0.0
Thanks in advance.
WHY?
void do_p(const char *cat, int level, const char *Format ...); // ok
Presumably because cppcheck doesn't recognise const char *Format ... as a printf format and variadic arguments unless they are separated by comma, so you didn't get the error.
void do_p(const char *cat, int level, const char *Format, ...); // error
You configured cppcheck to check bad format / argument pairing, so this is where you should expect an error.
1st, modify the Cpp member function code to C-style function
2nd, remove overload funcions

Callback function with function pointer through DLL with Run-Time Check Failure #0 error

I had two projects which are written by C/C++.
Project 1 output is exe file and named MyProject.
Project 2 output is dll file and named Bridge.
I try to let Bridge to execute the function in MyProject. The function seems work, but I encounter an error
"Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention."
I doubt that the root cause may happen in __cdecl __stdcall convention, but still don't know how to solve it. All the project's Calling convention setting are __cdecl(/Gd), and VS IDE is VS 2013.
DLL Project (Bridge) Code
Header
typedef void(*sfcTrace)(const char *logLevel, const char *logMessage);
class OnRequestHandler
{
public:
virtual void writeLog(sfcTrace callback) = 0;
};
class GenericInfoHandler : public OnRequestHandler
{
public:
GenericInfoHandler();
~GenericInfoHandler() { delete this; };
void writeLog(sfcTrace callback);
};
extern "C" __declspec (dllexport) OnRequestHandler* __cdecl oneBridgeCallBack()
{
return new GenericInfoHandler;
}
CPP
void GenericInfoHandler::writeLog(sfcTrace callback)
{
const char *level = "DEBUG";
const char *message = "TEST";
callback(level, message);
}
MyProject Source code:
typedef OnRequestHandler* (__cdecl *test)();
HINSTANCE getDLL = LoadLibrary("Bridge.dll");
if (!getDLL)
{
cout << "Cannot not load DLL." << endl;
}
test func = (test)::GetProcAddress(getDLL, "oneBridgeCallBack");
if (!func)
{
cout << "Cannot not locate the function." << endl;
}
OnRequestHandler* instance = func();
instance->writeLog(&MyProject::TestCallBack); <----- Error Occurs here
Function implementation in MyProject
void MyProject::TestCallBack(const char *level, const char *message)
{
if (strcmp(level, "INFO") == 0){
// do something
}
else if (strcmp(level, "DEBUG") == 0){
// do something
}
}
Header:
typedef void(MyProject::*TestCallBack)(const char *logLevel, const char *logMessage);
class OnRequestHandler
{
public:
virtual void writeLog(sfcTrace callback) = 0;
};
class GenericInfoHandler : public OnRequestHandler
{
public:
GenericInfoHandler();
~GenericInfoHandler() { delete this; };
void writeLog(sfcTrace callback);
};
Try the following: (I am presuming that MyProject::TestCallback is not a static method, and it is it's use as a plain function that causes the stack corruption)
Update
typedef void(*sfcTrace)(const char *logLevel, const char *logMessage); to take an additional void * argument that will be supplied alongside the callback. I.e.:
typedef void(*sfcTrace)(void *cb_arg, const char *logLevel, const char *logMessage);
^^^^^^^^^^^^
Then update OnRequestHandler::writeLog(sfcTrace callback) and its overrides to take the additional argument from the caller. I.e.,
virtual void OnRequestHandler::writeLog(sfcTrace callback, void *cb_arg) = 0;
^^^^^^^^^^^^
void GenericInfoHandler::writeLog(sfcTrace callback, void *cb_arg);
^^^^^^^^^^^^
Update the implementation of writeLog to pass this new arg to the callback:
void GenericInfoHandler::writeLog(sfcTrace callback, void *cb_arg)
{ ^^^^^^^^^^^^
const char *level = "DEBUG";
const char *message = "TEST";
callback(cb_arg, level, message);
} ^^^^^^
Now we can write a new non-member callback function that is capable of calling a MyProject object's methods, so long as a MyProject object is passed via cb_arg:
void myprj_method_callback(void *arg, const char *logLevel, const char *logMessage) {
MyProject *mp = (MyProject *)arg;
mp->TestCallBack(level, message);
}
Finally, we can update the call to pass the new call-back function, and argument:
instance->writeLog(myprj_method_callback, (void *)this);
As an aside, it is generally good practice on all callbacks to let the callback supplier pass an argument that will in turn be passed to the callback when it is called. This lets the user of a callback mechanism convey the relevant data-structures to the callback function without having to store them in global variables.

Is it normal for strstr()?

Description:
Declaration of strstr:
char *strstr(const char *haystack, const char *needle);
Definition of my function:
hostinfo_t *extract_host_from_url(const char *url) {
/* ... */
char *scheme_pos = strstr(url, "://");
/* ... */
}
How I use it:
void rewrite_url(string &url) {
/* ... */
hostinfo_t * hostinfo = extract_host_from_url(url.c_str());
/* ... */
}
Error info:
error: invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive]
char *scheme_pos = strstr(url, "://");
Question:
Where do things go wrong?
C++ declaration of strstr, as given in <cstring> is overloaded
const char* strstr( const char* str, const char* target );
char* strstr( char* str, const char* target );
With your set of arguments, you are calling the first function, which is why the return type is const char*.

C++ errors, maybe coming from .h files, not sure

Ok, I am trying to compile a program:
g++ -std=c++0x a1test.cpp GS1Prefix.cpp EAN.cpp
But I am getting errors that I never seen before.
In file included from a1test.cpp:17:0:
EAN.h:3:25: error: âPrefixâ does not name a type
EAN.h:3:33: error: ISO C++ forbids declaration of âpâ with no type [-fpermissive]
a1test.cpp: In function âbool RegisteredTests(const Prefix*, int&, int*, int*)â:
a1test.cpp:222:68: error: no matching function for call to âisRegistered(const Prefix*&, const char [14], char [6], char [8], char [7])â
a1test.cpp:222:68: note: candidates are:
EAN.h:3:6: note: bool isRegistered(const int*, const char*, char*, char*, char*)
EAN.h:3:6: note: no known conversion for argument 1 from âconst Prefix*â to âconst int*â
GS1Prefix.h:10:6: note: bool isRegistered(const Prefix*, int)
GS1Prefix.h:10:6: note: candidate expects 2 arguments, 5 provided
This is just half of the errors, below i have all the .h files and a link to a1test.cpp (its a long code)
EAN.h
bool isValid(const char* str);
bool isRegistered(const Prefix* p, const char* str, char area[],char publisher[], char title[]);
GS1Prefix.h
const int MAX = 700;
struct Prefix {
int no; // number of entries
int area[MAX]; // area elements
char pubLow[MAX][8]; // low end of publisher range
char pubHgh[MAX][8]; // high end of publisher range
int pubLen[MAX]; // no of chars in publisher string
};
bool load(const char* filename, Prefix* p);
bool isRegistered(const Prefix* p, int area);
int minNoDigits(const Prefix* p, int area);
bool isRegistered(const Prefix* p, int area, const char* publisher);
Link to a1test.cpp ->a1test.cpp
UPDATE: As suggested by remyabel, i made #include "GS1Prefix.h" after #include "EAN.h"
In file included from EAN.cpp:6:0:
EAN.h:3:25: error: âPrefixâ does not name a type
EAN.h:3:33: error: ISO C++ forbids declaration of âpâ with no type [-fpermissive]
Simply switch the order of your headers and your code should compile fine. Because you did not provide GS1Prefix.cpp and EAN.cpp I cannot comment on the rest of the errors (if there are any left.)
#include <cstring>
// GS1Prefix.h
const int MAX = 700;
struct Prefix {
int no; // number of entries
int area[MAX]; // area elements
char pubLow[MAX][8]; // low end of publisher range
char pubHgh[MAX][8]; // high end of publisher range
int pubLen[MAX]; // no of chars in publisher string
};
// EAN.h
bool isValid(const char* str);
bool isRegistered(const Prefix* p, const char* str, char area[],char publisher[], char title[]);
// a1test.cpp
bool load(const char* filename, Prefix* p);
bool isRegistered(const Prefix* p, int area);
int minNoDigits(const Prefix* p, int area);
bool isRegistered(const Prefix* p, int area, const char* publisher);
// .. rest of your file
Header files should include sufficient definition so that they are self-sufficient. In this case you need to supply a definition of the requisite type, Prefix. Just add the include to the top of EAN.h:
#include <GS1Prefix.h>
bool isValid(const char* str);
bool isRegistered(const Prefix* p, const char* str, char area[],char publisher[],
char title[]);
Then you can include EAN.h in any source file knowing that you don't have dependencies to worry about.

SWIG-generated Lua<-->C++ Wrapper mishandling primitive types renamed by typedef

I use SWIG to generate a C++ <--> Lua wrapper for a work project.
my main problem is, in this project at the base there exist type definitions for each platform. E.g. for Win32 there exists a header Win32Types.h where things like
typedef char Char;
typedef char TChar;
typedef signed int Int;
typedef unsigned int UInt;
typedef signed char Int8;
typedef unsigned char UInt8;
...
are defined.
The problem is now, with an example class like
class Named
{
public:
Named();
virtual ~Named();
void setName(const Char *name);
const Char* GetName() const;
}
, the setName- Method generated in the SWIG-wrapper looks something like this:
static int _wrap_Named_SetName(lua_State* L) {
int SWIG_arg = 0;
Named *arg1 = (Named *) 0 ;
Char *arg2 = (Char *) 0 ;
SWIG_check_num_args("Named::SetName",2,2)
if(!SWIG_isptrtype(L,1))
SWIG_fail_arg("Named::SetName",1,"Named *");
if(!SWIG_isptrtype(L,2))
SWIG_fail_arg("Named::SetName",2,"Char const *");
if (!SWIG_IsOK(SWIG_ConvertPtr(L,1,(void**)&arg1,SWIGTYPE_p_Named,0))){
SWIG_fail_ptr("Named_SetName",1,SWIGTYPE_p_Named);
}
if (!SWIG_IsOK(SWIG_ConvertPtr(L,2,(void**)&arg2,SWIGTYPE_p_Char,0))){
SWIG_fail_ptr("Named_SetName",2,SWIGTYPE_p_Char);
}
...
}
the problem here is, the wrapper tries to treat Char as just another class pointer, although it is just a char pointer renamed to Char.
is there any way to circumvent this behaviour?
i tried to write a typemap like
%typemap(in) Char {
$1 = lua_tostring($input);
}
, but im not sure i did it the right way...
There's two easier ways you can do this:
Show SWIG the typedefs for that platform, probably using %include
Tell SWIG to just use the normal unsigned char * typemap using %apply:
%apply unsigned char * { const Char * }