Monday, January 18, 2010

Smashing The Stack

This is the first post in a "series" I'll be writing. It's primarily going to be a pointer to a post I find interesting on StackOverflow. Often this will be a post that I've answered or asked, but hey thats just my bias ;)
So this first article is about calling C++ functions and classes from C. So this solution covers the simple case. Usesr asked about how we can handle exceptions and allocating small objects on the stack rather than the heap. I'm going to show you a way that these can be addressed.
There's a few key issues.
  1. Any exception that hits the C++/C bridge will crash your application
  2. You can only catch exceptions in C++
  3. Catching _all_ exceptions by catch(...) can leave your application in an invalid/unrecoverable state. Some compilers (VC++) will allow catch(...) to catch things like segmentation faults etc. The only sane  thing to do in that case is immediately exit. 
  4. Other exception may be recoverable, and we need a way to handle this.
Here's the source of the C++ class we're going to access from C
 1 #pragma once
 2
 3 #include <iostream>
 4 #include <map>
 5
 6 class MyMap
 7 {
 8   public:
 9     struct Exception
10     {
11       explicit Exception(const std::string & mesg) : mesg_(mesg) {}
12       std::string mesg_;
13     };
14
15   void addKV( const std::string & key, const std::pair<int,int> & value );
16   std::pair<int,int> getV( const std::string & key ) const;
17   std::map<std::string,std::pair<int,int> > map_;
18 };

Heres what my C header looks like

 1 #pragma once
 2
 3 typedef struct
 4 {
 5   int x;
 6   int y;
 7 } CLocation;
 8
 9 typedef struct HMyMap HMyMap;
10
11 HMyMap* MyMap_create( void(*handler)(const char*) );
12 void MyMap_destroy( HMyMap *h );
13
14 void MyMap_addKV( HMyMap *h, const char *k, CLocation v );
15 CLocation MyMap_getV( HMyMap *h, const char *k );

And the implementation:

 1 extern "C"
 2 {
 3 #include "simpletest.h"
 4 }
 5 #include "MyClass.h"
 6
 7 std::pair<int,int> pair_from_cloc( const CLocation & loc )
 8 {
 9   return std::make_pair(loc.x,loc.y);
10 }
11
12 CLocation cloc_from_pair( const std::pair<int,int> & loc )
13 {
14   CLocation cloc = { loc.first, loc.second };
15   return cloc;
16 }
17
18 struct HMyMap : public MyMap
19 {
20   HMyMap( void(*eh)(const char*) ) : MyMap(), handler(eh) {}
21   void(*handler)(const char*);
22 };
23
24 HMyMap* MyMap_create( void(*eh)(const char*) )
25 {
26   return new HMyMap(eh);
27 }
28
29 void MyMap_destroy( HMyMap *h )
30 {
31   delete h;
32 }
33
34 void MyMap_addKV( HMyMap *h, const char *k, CLocation v )
35   try
36 {
37   h->addKV( k, pair_from_cloc(v) );
38 }
39 catch( MyMap::Exception & e )
40 {
41   if( ! h->handler ) throw;
42   h->handler( e.mesg_.c_str() );
43 }
44
45 CLocation MyMap_getV( HMyMap *h, const char *k )
46   try
47 {
48   return cloc_from_pair( h->getV(k) );
49 }
50 catch( MyMap::Exception & e )
51 {
52   if( ! h->handler ) throw;
53   h->handler( e.mesg_.c_str() );
54   return cloc_from_pair(std::make_pair<int,int>(0,0));
55 }
Things to note are exposing std::pair<int,int> as CLocation struct, (rather than opaque handle). Allowing specification of an exception handler in the HMyMap class.
I'm not 100% happy with the way the exception handling is done, and will show another option in another post.

No comments:

Post a Comment