Abstracting C Platform Query Calls in Type and Memory Safe C++ Containers a.k.a. RAII Using Enums and Templates

biq
biq
Oct 24, 2020 · 4 min read
Image for post
Image for post
Safety first, everyone.

Intro

While intergrating native platform C APIs (e.g. on windows) into a third-party C++ application, I found myself having to make sure that query functions, that allocate and fill results of different data types, are called with the right paramters. And the inquired data is also properly disposed of after the call. This, and the fact that saftey is on the forefront of the collective unconcious, served as the inspiration for this post and the publishing of a small demo, highlighting the architecture proposed in this article. Hope you enjoy reading.

Setup

The goal was, to make the abstraction able to select the appropriate data for the query call on the basis of a type indicated by an enum value. The desired usage of the abstraction should look like the following:

Creating a query which returns a result with data type bool.

Given the operation type native_query_is_valid the query should yield a boolean value after its execution. It should also be possible to ask the query for its state after executing the native call and if the result has a valid value.

Image for post
Image for post

Let’s pretend we are working with a native platform, that provides some feature in form of a C interface. The feature queries the platform on the basis of an enum value and provides you with a result, which can be of various data types.

Mock C interface of our imagined native platform library.

The two available types in this example, are a boolean and a data structure called NATIVE_DESCRIPTION. To indicate the query type, the API defines an enum called NATIVE_OPERATION_CODE. To query the system, you call SomeNativeQueryMethod, which expects a handle, the type of operation we are looking to execute, the size of the data provided and a pointer to a pointer of type void. When calling the function, the memory pointed to by the variable you passed to the paramter Data will be filled with either a boolean or description data structure, depending on which operation type you passed to the Operation paramter.

The journey to abstraction

Neatly packaged.

Let’s start by abstracting the way we interact with the memory which will be filled by the query function.

Memory management container.

This container is created in the spirit of ‘resource acquisition is initialization’ (RAII) principal. The base idea is, that an objects life cycle, should coincide with the allocation and deallocation of the internal memory used. In our case, the focus is primarly on the deallocation part, as can be seen in the deconstructor.

Next we setup an interface to encapsulate a query. It abstracts the execution of the native query method, by story the type of operation and has memory to store the result in. It also knows how to get the size of the result data and check if the data is valid. Now there is no actual way, to access the result data. Which we will be looking at next.

Stay safe out there!

With the base class NativeQueryBase handling all the non type specific functionallity, we know have to find a way to access our data depending on which enum value the query value has. This is done in two steps. First we declare a template class NativeQuery, with the default type of pointer to void.

Query class template.

Now as specified in the beginning, we want to expose two types of native queries, native_query_is_valid with the resulting data being a boolean and native_query_description with the resulting data being a custom structure. For this purpse, we create two macros. DECLARE_QUERY_TYPE will declare a template specialization, of a certain operation type (our platform enum) and the corresponding return type. The return type is used to declare a get methods which has the expected/appropriate type for its operation. The second is the defintion of the template specialization class’ constructor, specifying the size of the data type and implementing the actual getter, which merely casts the data to the expected type. This allows us to now declare two types of query operations, with their respective return type.

Template macros declarations.

And then define the implementation in any compilation unit we want.

Implementation of template class via macro.

Tying it all together

Now to run an actual query, all we have to do, is create a query object and specify the type of query we want to execute. The compiler will validate and choose the appropriate template implementation for us and will bless us with an error if there is something wrong. We also don’t have to worry about the result data, as it is managed be the life cylcle of the query object.

Running type and memory safe quries.

This is it for now 🎉 Until next time my friends. Thank you for reading!

The Startup

Medium's largest active publication, followed by +756K people. Follow to join our community.

biq

Written by

biq

Today’s impossible, will be tomorrow’s reality. https://think-biq.com

The Startup

Medium's largest active publication, followed by +756K people. Follow to join our community.

biq

Written by

biq

Today’s impossible, will be tomorrow’s reality. https://think-biq.com

The Startup

Medium's largest active publication, followed by +756K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store