How to implement a shared class in a C-style API for a C++ DLL

I’m currently developing a C++ library and am creating a C-style API for it. Initially, I had several methods utilizing a singleton instance of the primary class. Here’s a simplified version of the declaration:

extern "C" {
    MYLIB_API void fetchAudioDevices();
    MYLIB_API void fetchWindowHandles();

    MYLIB_API uint32_t startVideoRecording();
    MYLIB_API uint32_t endVideoRecording();
}

However, I have decided to eliminate the use of singletons. This presents a challenge: what is the best approach to utilize my class via these API calls? One solution I considered is employing a global variable along with an initialization function, similar to this (where VideoProcessor is a C struct that wraps a C++ class):

extern "C" {
    VideoProcessor* processor = new VideoProcessor();

    MYLIB_API void fetchAudioDevices();
    MYLIB_API void fetchWindowHandles();

    MYLIB_API uint32_t startVideoRecording();
    MYLIB_API uint32_t endVideoRecording();
}

Alternatively, should I define a C struct that encapsulates all API methods and a pointer to the VideoProcessor object, exporting the struct itself? I appreciate any insights or suggestions on this matter. Thank you!

To integrate your C++ class with a C-style API efficiently, avoid singletons and global variables prone to thread issues. Instead, use opaque pointers. Here’s a typical approach:

// C++ code
class VideoProcessor {
    // your implementation
};

extern "C" {
    struct VideoProcessorWrapper;

    MYLIB_API VideoProcessorWrapper* VideoProcessor_new() {
        return reinterpret_cast<VideoProcessorWrapper*>(new VideoProcessor());
    }

    MYLIB_API void VideoProcessor_delete(VideoProcessorWrapper* vp) {
        delete reinterpret_cast<VideoProcessor*>(vp);
    }

    MYLIB_API void fetchAudioDevices(VideoProcessorWrapper* vp) {
        // Use vp->fetchAudioDevices();
    }

    // Other API methods utilizing VideoProcessorWrapper*
}

This way, each client manages its instance, and concurrency issues are minimized. Use VideoProcessor_new() and VideoProcessor_delete() to manage lifecycles.