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