Now, let's detour a bit to "native" C++11 interface. The "native" C++11 interface is known as Compilation Firewalls, an opaque C++ interface "on steroid". These are the relevant articles on it:
- GotW #100: Compilation Firewalls (Difficulty: 6/10)
- Pimpl For Compile-Time Encapsulation (Modern C++)
Let's get back to our problem. Let's restate the problem into a more manageable question: How to wrap opaque C pointer into C++11 smart pointer (specifically unique_ptr)? Short answer: Use the opaque C pointer object type as parameter to unique_ptr template and provide a custom deleter. That's it. If you understand the short answer, then you're done. If it's still unclear to you, then read on.
There are two kinds of unique_ptr template, one with only one template parameter (because the second parameter has default value) and one with two template parameters--see: http://en.cppreference.com/w/cpp/memory/unique_ptr. You need to use the unique_ptr template with two template parameters to wrap an opaque C pointer because you need to provide a custom deleter. A custom deleter is a function that finalize/deallocate the resources allocated by the unique_ptr constructor. Let's look at an example:
unique_ptr<FILE, int (*)(FILE*)> mFile{nullptr, closeFile};
The preceding code snippet shows the second unique_ptr template parameter is a function pointer. The function pointer is initialized with closeFile function name. In this case, the closeFile function pointer is the custom deleter. closeFunction() is a simple function which logs a message to the screen and then call fclose(), as shown in the following code snippet:
int closeFile(FILE* f) { cout << "Calling fclose()" << endl; return fclose(f); }
You might be thinking how to obtain a valid FILE pointer in the first place, before disposing it with fclose(). Well, of course with a call to fopen(). This is how I do it:
public: explicit FileHandler (const char* path, const char* mode) try: mPath{path}, mMode {mode} { cout << "FileHandler constructor" << endl; FILE* f = fopen(path, mode); if (f != NULL) { unique_ptr <FILE, int (*)(FILE*)> file{f, closeFile}; mFile = std::move(file); } else { throw FileHandlerException(errno, "Failed to open " + string(path)); } } catch (FileHandlerException& e) { throw e; }
The preceding code snippet shows that a valid FILE pointer is passed as parameter to the unique_ptr object that will manage the FILE pointer upon its initialization. Then, the object is moved to the equivalent class member which will manage the FILE pointer. You can clone the complete code at: https://bitbucket.org/pinczakko/custom-c-11-deleter
The sample shows how to wrap the opaque FILE pointer in a unique_ptr smart pointer. The FILE pointer is an opaque "standard" C library pointer. Despite this, the technique is applicable to other opaque C pointer which usually acts as interface to a third party C library that you want to use in your C++11 application.
NOTE: The syntax of the FileHandler class constructor might be a bit alien to you if you're not familiar with C++11. It's called function-try-block, a way to wrap the whole function inside a try-catch block. The complete explanation is at: http://en.cppreference.com/w/cpp/language/function-try-block, additional heavily commented sample is at: https://msdn.microsoft.com/en-us/library/e9etx778(v=vs.120).aspx.
I hope this post is helpful to those looking to unleash the power of C libraries in C++11.
Post a Comment
No comments:
Post a Comment