C++ How to create unique types, from the all stack template. Part two.

Same Template Different Types
Same Template Different Types

Some real life context to this post might be here. And what is going on in here? Conceptually.Structs with only static methods, when instantiated,  are resulting in all instances sharing the same methods inside.

Same as static members, static methods are shared between all instances. Meaning: no actual different objects can be created out of the above.

Those objects technically are different. But methods are not. Them objects all share the same method. Very much like having the namespace with a function inside.

Now if we make this into the template we suddenly raise the complexity.

A template is not a type. From the above template, we can make only two types aka template definitions.

Now we have two types. Each type having only one shared method inside. Both being a method m1() declared in the template. Thus:

The outcome of using a template is we got different types. We share m1() on the type level. Because it is static.  Thus we are narrowing the scope of sharing. By using particular template arguments.

C++ complexity is a direct consequence of C++ flexibility. And sometimes we need it.

A real-life use case.

I had the same problem in my dbj SQL suite. An SQLite standard C++ API. A simple template with “nothing” inside that has to be made into separate unique types when defined. I had to narrow the scope of sharing of a static method inside a template.

UDF is user-defined function. Crucial for the whole dbj SQL framework is to offer “simple udf’s” functionality for the customers.  And I wanted to encapsulate this, in order to decouple the users from a  complex sqlite3 C API.

SQLite API provides one function to register UDF’s. And it is to be used like so:

Where udf is a C/C++ function pointer, that must conform to exact signature required.

To use this in it’s “naked” form, for 99% of users is “not fun”. And hardly leads to reusable code. Thus they resort to C++ wrapper like mine is.

So, what is the point?

What I am sharing here is how I have designed and developed this “easy UDF’s”. Let’s start with the key C++ abstraction.

Users see and use the above. Using that, inside my implementation, I have to call the bellow, eventually.

I wrap the instance of the “easy udf” above, to be used by the required template definition.

Key point: we give exact function pointer of “easy udf” as template argument, thus different types are created when this template is turned into a type.

The template.

A template with just one static function inside.  Let me repeat: that one template argument is not a type, it is a value argument. A function pointer type.

Why is it done this way?

Repetition: A template in itself is not a type. Types are made from templates that are given concrete template parameters and turned into template definitions.   Template definition is a type. Function pointer as a template argument is providing identity to the definitions made from this template. Example.

udf_holder template, has no data and no functions, only one static method. To be shared among all types made from it.

The template argument is a function pointer. Each udf is a unique function pointer, thus providing a unique identity to the types udf_h_1 and udf_h_2 above.

Crucial point: udf_h_1 and udf_h_2 are two completely different types.

Ok, but why is this function static?

Because we need to cast it to non-member aka “free-standing” function pointer type. And that function is required by SQLite3 C API.  How do we transform it from C++ static template function to free-standing function:

Address of C++ class static function can be assigned to C function pointer. You can not do that with “normal” non-static C++ class function.

The API

Basically, I have a standard callback function, that is always the same. From which, in turn, I call different SQLite user-defined “easy udf” functions. Here is the one method from dbj++sql.h in my SQLite C++ API that does the transformation into C function and registration with the SQLite:

All of the above C++ sorcery is hidden away and happy users make happy standard C++ functions to be used as SQLite “easy udf’s”, each in one simple call:

 

Same as ever, if anybody requires more clarifications, do not be afraid to comment.  Enjoy the standard C++.