Interfacing Sing and C++
Calling Sing from C++
As mentioned before, sing is a tool to write c++.
Once you have compiled sing, take a look at the generated .cpp and .h files to know which functions and types have been generated and how to use them.
Calling Cpp from Sing
This is the difficult part. Sing uses a subset of the C++ language. There is therefore no guarantee that you can call c++ from sing.
Here are the c++ types that Sing supports (and their equivalent sing types):
​
c++ type sing type passing (in) passing (out)
​
int8_t i8 value pointer
int16_t i16 value pointer
int32_t i32 value pointer
int64_t i64 value pointer
uint8_t u8 value pointer
uint16_t u16 value pointer
uint32_t u32 value pointer
uint64_t u64 value pointer
float f32 value pointer
double f64 value pointer
std::complex<float> c64 value pointer
std::complex<double> c128 value pointer
bool bool value pointer
std::string string const char * pointer
sing::array<type, n> [n] type const ref pointer
std::vector<type> [*] type const ref pointer
sing::map<key, elem> map(key) elem const ref pointer
std::shared_ptr<type> * type value pointer
std::weak_ptr<type> weak * type value pointer
function pointer (args) type value pointer
enum class enum value pointer
class class/interface const ref pointer
​
As long as a c++ module uses only those types and compatible parameter passing methods, you can use it from sing.
If instead you need to access different functions and types, you need to consider either:
-
to change them to make them sing-compatible.
-
to write a stub
To invoke C++ code from Sing you also need to create a "Sing declaration file" with the equivalent of the C++ types and C++ function signatures. (you can't directly insert a c++ header in a require directive because the sing compiler is not able to parse c++ headers).
​
Sing declaration files
The sing declaration file is a plain sing file containing all the public types that you are going to use and all the public functions. In the declaration file you replace the functions' bodies with a ';' character.
It is not possible to compile sing declaration files (if you try you get errors) but you can put them in require directives to get access to the implementing C++ code.
​
Note that you are not forced to declare in a sing declaration file all the fields of a class. You can hide to sing several fields of a c++ class for a number of reasons. For example because you don't want sing to access those fields, because those fields don't map to a Sing type, etc..
Also, since the implementation is directly written in c++, there is no need for sing to know any private member of the class.
​
for example:
​
class Base {
public:
void BaseFunction(void);
};
​
class NamesList : Base {
vector<char> _names;
vector<uint32_t> _indices;
void TerminateAndCreateIndices(void);
public:
void AddName(const char *name);
void AddCrLfTerminatedName(const char *name);
void Sort(void);
void DeleteDuplicated(void);
const char *GetName(uint32_t index) const;
int CompareStrings(uint32_t i1, uint32_t i2);
int GetNamesCount(void) const { return(_indices.size()); }
void Reset(void) { _names.clear();_indices.clear(); }
void Erase(int first, int past_last);
};
​
may map just to:
​
public class NamesList {
public:
fn mut BaseFunction() void;
fn mut AddName(name string) void;
fn mut Sort() void;
fn GetNamesCount() i32;
}
​
If you are interested only in those functions.
Note that:
-
You will be able anyway to instance and copy the class. That's because, even if sing only has a partial definition, the generated c++ code has full access to the definition and spawns/copies the instance properly.
-
Unless the original function is const, the sing function must be mut.
-
You can hide to sing the fact that one or more of the functions are implemented into other base classes.
​
Interfaces
Interfaces are tricky, because Sing puts special code in Interfaces to make typeswitch possible.
You have 2 options:
-
Ignore this fact and carefully avoid to use typeswitch on classes derived from a native c++ interface (a class with virtual functions).
-
Add some code to your c++ class to make it sing compliant.
This is an exmaple of a c++ class mapped into a sing interface:
class INamesList {
public:
virtual void AddName(const char *name) = 0;
virtual void AddCrLfTerminatedName(const char *name) = 0;
void Sort(void) = 0;
void DeleteDuplicated(void);
};
​
can become:
​
interface INamesList {
fn mut AddName(name string) void;
fn mut AddCrLfTerminatedName() void;
}
​
If you need to use a typeswitch on classes originally implemented in c++, you can opt to add some code to those classes. Here is the code.
​
Required in c++ classes that are used in a typeswitch expression:
public:
virtual ~<classname>() {}
virtual void *get__id() const = 0;
​
Required in c++ classes that are used as case options in a typeswitch
public:
virtual void *get__id() const override { return(&id__); };
static char id__;
​
Required in c++ classes that are used as case options in a typeswitch, in the implementation file (cpp)
char <classname>::id__;
The -p compiler option
If you run the sing compiler with the -p option it digests a sing declaration file without issuing errors and generates a compatible .h file. This is pretty useful if you need to write some c++ stubs or if you need to write some functions in c++ because sing is missing some important c++ feature that you want to leverage.
You can proceed as follow:
1) you write a sing declaration file with the declarations and signatures of the functions you need.
2) you run the compiler with the -p option:
​
sing -p -o somefile.h somefile.sing
​
3) Then you implement in somefile.cpp the functions using the signatures from somefile.h
​