Introduction
In the previous article (Part 1) we concentrated on getting setup to build our Apache custom module. In this article we will look at adding our first C++ class that steps us from the world of Apache C into the C++ domain.
Our first class, CApplication
So let's begin where we left off from the previous article. Below is the code for the
foo_handler()
function, let's recap and take a closer look:-
EXTERN_C_FUNC
int foo_handler( request_rec* inpRequest )
{
int nReturnVal = DECLINED;
if ( inpRequest->handler != NULL && strcmp( inpRequest->handler, "foo" ) == 0 )
{
ap_rputs( "Hello World from FOO", inpRequest );
nReturnVal = OK;
}
return nReturnVal;
}
The first thing to notice here is that the handler is invoked with a pointer to the Apache API request record
request_rec* inpRequest
. In turn that pointer is used to get some data (the handler name, "foo") and used in output (within
ap_rputs()
function). Now lets introduce our first class, CApplication. Create the following two files in you Netbeans project:
- Header Files > Application > CApplication.hpp
- Source Files > Application > CApplication.cpp
Now in these files here's the code:
CApplication.hpp
#ifndef CAPPLICATION_HPP
#define CAPPLICATION_HPP
#include "mod_foo.hpp"
class CApplication
{
private:
request_rec* m_pRequestRec;
public:
CApplication( request_rec* inpRequestRec ) :
m_pRequestRec( inpRequestRec )
{}
int RunHandler();
};
#endif /* CAPPLICATION_HPP */
CApplication.cpp
#include "CApplication.hpp"
int
CApplication::RunHandler()
{
int nReturnVal = DECLINED;
if ( inpRequest->handler != NULL && strcmp( inpRequest->handler, "foo" ) == 0 )
{
ap_rputs( "Hello World from FOO", inpRequest );
nReturnVal = OK;
}
return nReturnVal;
}
As you can see the core functionality has been moved from foo_handler()
to
CApplication::RunHandler()
. Now all we have to do is go back to mod_foo.cpp and change it to create an instance of our application and call it's
CApplication::RunHandler()
method thus (note, I have added comments to describe what each section does):
#include "mod_foo.hpp"
#include "CApplication.hpp"
EXTERN_C_BLOCK_BEGIN
typedef struct
{
void* vpCApplication;
}
FOOCONFIG_t;
EXTERN_C_BLOCK_END
EXTERN_C_FUNC
void foo_register_config_ptr( request_rec* inpRequest, FOOCONFIG_t* inpFooConfig );
EXTERN_C_FUNC
FOOCONFIG_t* foo_get_config_ptr( request_rec* inpRequest );
EXTERN_C_FUNC
apr_status_t foo_delete_capplication_object( void* inPtr )
{
if ( inPtr )
delete ( CApplication* )inPtr;
return OK;
}
EXTERN_C_FUNC
int foo_handler( request_rec* inpRequest )
{
CApplication* pApp = new CApplication( inpRequest );
if ( pApp == NULL )
return HTTP_SERVICE_UNAVAILABLE;
apr_pool_cleanup_register(
inpRequest->pool,
( void* )pApp,
foo_delete_capplication_object,
apr_pool_cleanup_null
);
FOOCONFIG_t* pFooConfig =
( FOOCONFIG_t* ) apr_palloc(
inpRequest->pool, sizeof( FOOCONFIG_t ) );
pFooConfig->vpCApplication = ( void* )pApp;
foo_register_config_ptr( inpRequest, pFooConfig );
return pApp->RunHandler();
}
EXTERN_C_FUNC
void foo_hooks( apr_pool_t* inpPool )
{
ap_hook_handler( foo_handler, NULL, NULL, APR_HOOK_MIDDLE );
}
EXTERN_C_BLOCK_BEGIN
module AP_MODULE_DECLARE_DATA foo_module =
{
STANDARD20_MODULE_STUFF,
NULL,
NULL,
NULL,
NULL,
NULL,
foo_hooks
};
EXTERN_C_BLOCK_END
EXTERN_C_FUNC
void foo_register_capplication_ptr( request_rec* inpRequest, FOOCONFIG_t* inpPtr )
{
ap_set_module_config( inpRequest->request_config, &foo_module, ( void* )inpPtr );
}
EXTERN_C_FUNC
FOOCONFIG_t* foo_get_capplication_ptr( request_rec* inpRequest )
{
FOOCONFIG_t* pReturnValue = NULL;
if ( inpRequest != NULL )
{
pReturnValue =
( FOOCONFIG_t* )ap_get_module_config(
inpRequest->request_config, &foo_module );
}
return pReturnValue;
}
As you can see we have now made the transition from the C function foo_handler()
to the C++
CApplication::RunHandler()
and we are in the world of C++. In the handler we create a new instance of our application.
To ensure our application does not leak memory a C clean-up function is registered with the request_rec->pool garbage collection system
which is responsible for calling delete on our application pointer pApp
at the end of the request cycle.
Finally we reserve a small amount of memory from the request_rec->pool to hold a pointer to our application configuration data structure which is then regsitered with the Apache API.
Although not used at this stage in this article this allows us to recover the pointer to our application
pApp
in other Apache hooks after
the handler is complete but before the request_rec->pool is destroyed. In later articles we will see how this recovered pointer is useful for handling
Apache output filter hooks that run after the handler function has completed and returned.
Conclusion
We have seen that transferring control from an Apache C handler to a C++ object is fairly straight forward. In the next article we will look at the the beginnings of abstracting the Apache API into a more well defined OOPs orientated set of objects.