I wanted to use a C++ library in my C code and I did the following.
This is the original API in C++. The original code is Copyright (C) 2011-2018 Filipe Coelho <falktx at falktx.com> and is licensed under the GNU GPL, version 2 or later. Some parts are abbreviated.
/* CarlaPlugin.hpp */
namespace CarlaBackend
{
typedef enum {
PLUGIN_CATEGORY_NONE = 0,
PLUGIN_CATEGORY_SYNTH = 1,
PLUGIN_CATEGORY_DELAY = 2,
PLUGIN_CATEGORY_EQ = 3,
PLUGIN_CATEGORY_FILTER = 4,
PLUGIN_CATEGORY_DISTORTION = 5,
PLUGIN_CATEGORY_DYNAMICS = 6,
PLUGIN_CATEGORY_MODULATOR = 7,
PLUGIN_CATEGORY_UTILITY = 8,
PLUGIN_CATEGORY_OTHER = 9
} PluginCategory;
class CARLA_API CarlaPlugin
{
virtual PluginCategory getCategory() const noexcept;
uint32_t getAudioInCount() const noexcept;
virtual void getMaker(char* const strBuf) const noexcept;
virtual void showCustomUI(const bool yesNo);
virtual void getRealName(char* const strBuf) const noexcept;
virtual void uiIdle();
bool saveStateToFile(const char* const filename);
bool loadStateFromFile(const char* const filename);
virtual void process(const float** const audioIn, float** const audioOut,
const float** const cvIn, float** const cvOut, const uint32_t frames) = 0;
};
}
This is the header file for the interface. It works for both C and C++.
/* carla_plugin.h */
#ifndef __PLUGINS_CARLA_PLUGIN_INTERFACE_H__
#define __PLUGINS_CARLA_PLUGIN_INTERFACE_H__
#include <CarlaBackend.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void * CarlaPluginHandle;
void
carla_plugin_show_custom_ui (
CarlaPluginHandle handle,
const int show);
void
carla_plugin_ui_idle (
CarlaPluginHandle handle);
void
carla_plugin_process (
CarlaPluginHandle handle,
const float ** const audio_in,
float ** const audio_out,
const float ** const cv_in,
float ** const cv_out,
const uint32_t frames);
void
carla_plugin_get_real_name (
CarlaPluginHandle handle,
char * const name);
void
carla_plugin_get_maker (
CarlaPluginHandle handle,
char * const name);
int
carla_plugin_get_category (
CarlaPluginHandle handle);
uint32_t
carla_plugin_get_audio_in_count (
CarlaPluginHandle handle);
int
carla_plugin_save_state_to_file (
CarlaPluginHandle handle,
const char * const filename);
int
carla_plugin_load_state_from_file (
CarlaPluginHandle handle,
const char * const filename);
#ifdef __cplusplus
}
#endif
#endif // header guard
This is the interface "glue code" in C++. It should be compiled as a C++ file and linked together with the rest of the C code.
#include "carla_plugin.h"
#include <CarlaPlugin.hpp>
extern "C"
{
#define GET_PLUGIN \
CarlaBackend::CarlaPlugin * plugin = \
(CarlaBackend::CarlaPlugin *) handle
void
carla_plugin_show_custom_ui (
CarlaPluginHandle handle,
const int show)
{
GET_PLUGIN;
plugin->showCustomUI (show);
}
void
carla_plugin_ui_idle (
CarlaPluginHandle handle)
{
GET_PLUGIN;
plugin->uiIdle ();
}
void
carla_plugin_process (
CarlaPluginHandle handle,
const float ** const audio_in,
float ** const audio_out,
const float ** const cv_in,
float ** const cv_out,
const uint32_t frames)
{
GET_PLUGIN;
plugin->process (
audio_in, audio_out, cv_in, cv_out, frames);
}
void
carla_plugin_get_real_name (
CarlaPluginHandle handle,
char * const name)
{
GET_PLUGIN;
plugin->getRealName (name);
}
void
carla_plugin_get_maker (
CarlaPluginHandle handle,
char * const name)
{
GET_PLUGIN;
plugin->getMaker (name);
}
int
carla_plugin_get_category (
CarlaPluginHandle handle)
{
GET_PLUGIN;
return plugin->getCategory ();
}
uint32_t
carla_plugin_get_audio_in_count (
CarlaPluginHandle handle)
{
GET_PLUGIN;
return plugin->getAudioInCount ();
}
int
carla_plugin_save_state_to_file (
CarlaPluginHandle handle,
const char * const filename)
{
GET_PLUGIN;
return plugin->saveStateToFile (filename);
}
int
carla_plugin_load_state_from_file (
CarlaPluginHandle handle,
const char * const filename)
{
GET_PLUGIN;
return plugin->loadStateFromFile (filename);
}
}
This is an example usage:
/* some_code.c */
/* get a plugin handle through another similar interface
* (carla_engine) */
CarlaEngineHandle engine =
carla_engine_get_from_native_plugin (
self->descriptor, self->handle);
CarlaPluginHandle plugin =
carla_engine_get_plugin (
engine, self->carla_plugin_id);
/* use it */
carla_plugin_show_custom_ui (
plugin, 1);
The general idea is to hide the C++ class instance under a void * so it can be used in C, and instead of calling class functions on the class instance, call C functions normally by passing that pointer around.
The interface implementation converts that pointer back to the class instance and calls the instance methods.