{"id":197,"date":"2016-11-18T10:09:07","date_gmt":"2016-11-18T10:09:07","guid":{"rendered":"http:\/\/imalogic.com\/blog\/?p=197"},"modified":"2017-05-14T10:19:34","modified_gmt":"2017-05-14T10:19:34","slug":"hourglass-pattern","status":"publish","type":"post","link":"https:\/\/imalogic.com\/blog\/2016\/11\/18\/hourglass-pattern\/","title":{"rendered":"Hourglass pattern"},"content":{"rendered":"<body><h1>WRAPPER C\/C++<\/h1>\n<h2>DEVELOPING A C WRAPPER API FOR OO C++ CODE,<\/h2>\n<p>Develop a set of C APIs that will wrap around our existing C++ APIs to access our core logic (written in object-oriented C++). This will essentially be a glue API that allows our C++ logic to be usable by other languages. We based this development on a specific design pattern called hourglass pattern. All we ended up doing was: Every object is passed about in C an opaque handle. Constructors and destructors are wrapped in pure functions Member functions are pure functions. Other built-ins are mapped to C equivalents where possible.<\/p>\n<h2>FIRST APPROACH<\/h2>\n<p>So a class like this (C++ header)<\/p>\n<pre>class MyClass {\r\n\r\npublic:\r\n\r\nexplicit MyClass( std::string &amp; s );\r\n\r\n~MyClass(); int doSomething( int j );\r\n\r\n}<\/pre>\n<p>Would map to a C interface like this (C header):<\/p>\n<pre>struct HMyClass; \/\/ An opaque type that we'll use as a handle\r\n typedef struct HMyClass HMyClass;\r\n HMyClass * myStruct_create( const char * s );\r\n void myStruct_destroy( HMyClass * v );\r\n int myStruct_doSomething( HMyClass * v, int i );<\/pre>\n<p>The implementation of the interface would look like this (C++ source)<\/p>\n<pre>#include \"MyClass.h\"\r\n extern \"C\" {\r\n HMyClass * myStruct_create( const char * s ) {\r\n return reinterpret_cast&lt;HMyClass*&gt;( new MyClass( s ) );\r\n }\r\n void myStruct_destroy( HMyClass * v ) {\r\n delete reinterpret_cast&lt;HMyClass*&gt;(v);\r\n }\r\n int myStruct_doSomething( HMyClass * v, int i ) {\r\n return reinterpret_cast&lt;HMyClass*&gt;(v)-&gt;doSomething(i);\r\n }\r\n }<\/pre>\n<ul>\n<li>reinterpret_cast only guarantees that if you cast a pointer to a different type, and then reinterpret_cast it back to the original type, you get the original value\u2026<\/li>\n<li>static_casting a pointer to and from void* preserves the address<br>\nExceptions are not part of the C ABI so you cannot let Exceptions ever be thrown past<br>\nthe C++ code. So your header is going to look like this:<\/li>\n<\/ul>\n<pre> #ifdef __cplusplus\r\n extern \"C\"\r\n {\r\n #endif\r\n   void * myStruct_create( const char * s );\r\n   void myStruct_destroy( void * v );\r\n   int myStruct_doSomething( void * v, int i );\r\n #ifdef __cplusplus\r\n }\r\n #endif<\/pre>\n<p>And your wrapper\u2019s .cpp file will look like this:<\/p>\n<pre>void * myStruct_create( const char * s ) {\r\n MyStruct * ms = NULL;\r\n try { \/* The constructor for std::string may throw *\/\r\n   ms = new MyStruct(s);\r\n } catch (...) {}\r\n   return static_cast&lt;void*&gt;( ms );\r\n }<\/pre>\n<pre>void myStruct_destroy( void * v ) {\r\n  MyStruct * ms = static_cast&lt;MyStruct*&gt;(v);\r\n  delete ms;\r\n}\r\nint myStruct_doSomething( void * v, int i ) {\r\n MyStruct * ms = static_cast&lt;MyStruct*&gt;(v);\r\n int ret_value = -1; \/* Assuming that a negative value means error *\/\r\n try {\r\n   ret_value = ms-&gt;doSomething(i);\r\n } catch (...) {}\r\n   return ret_value;\r\n }\r\n}<\/pre>\n<p>Even better: If you know that all you need as a single instance of MyStruct, don\u2019t take the risk of<br>\ndealing with void pointers being passed to your API. Do something like this instead:<\/p>\n<pre>\r\n static MyStruct * _ms = NULL;\r\n int myStruct_create( const char * s ) {\r\n int ret_value = -1; \/* error *\/\r\n try { \/* The constructor for std::string may throw *\/\r\n _ms = new MyStruct(s);\r\n ret_value = 0; \/* success *\/\r\n } catch (...) {}\r\n return ret_value;\r\n }\r\n void myStruct_destroy() {\r\n if (_ms != NULL) {\r\n delete _ms;\r\n }\r\n }\r\n int myStruct_doSomething( int i ) {\r\n int ret_value = -1; \/* Assuming that a negative value means error *\/\r\n if (_ms != NULL) {\r\n try {\r\n ret_value = _ms-&gt;doSomething(i);\r\n } catch (...) {}\r\n }\r\n return ret_value;\r\n }<\/pre>\n<h2>OTHER POINTS<\/h2>\n<p>Namespace should be replaced by a prefix for each function.<br>\nTo be continued\u2026<\/p>\n<p><a href=\"https:\/\/imalogic.com\/blog\/wp-content\/uploads\/2016\/11\/IMA-HourGlassPattern.pdf\">The Hourglass pattern \u2013 Case study<\/a>\u00a0\u2013 PDF format<\/p>\n<\/body>","protected":false},"excerpt":{"rendered":"<p>WRAPPER C\/C++ DEVELOPING A C WRAPPER API FOR OO C++ CODE, Develop a set of C APIs that will wrap<\/p>\n","protected":false},"author":1,"featured_media":191,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[7],"tags":[],"class_list":["post-197","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-coding"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/imalogic.com\/blog\/wp-content\/uploads\/2016\/11\/Amanda-Hourglass.jpg?fit=3247%2C4000&ssl=1","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8J21V-3b","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/posts\/197","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/comments?post=197"}],"version-history":[{"count":2,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/posts\/197\/revisions"}],"predecessor-version":[{"id":338,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/posts\/197\/revisions\/338"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/media\/191"}],"wp:attachment":[{"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/media?parent=197"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/categories?post=197"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/tags?post=197"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}