GSLAM  3.0.0
Svar: A Tiny Modern C++ Header Brings Unified Interface for Different Languages

Svar is a part of GSLAM, but also is a separate project: https://github.com/zdzhaoyong/Svar

By Using Svar, your C++ library can be called easily with different languages like C++, Java, Python and Javascript.

Svar brings the following features:

Use Svar like JSON

Svar natually support JSON and here is some basic usage demo:

Svar null=nullptr;
Svar b=false;
Svar i=1;
Svar d=2.1;
Svar s="hello world";
Svar v={1,2,3}
Svar m={"b",false,"s","hello world"}
Svar obj;
obj["m"]=m;
obj["pi"]=3.14159;
std::cout<<obj;
std::stringstream sst("[2,3,4]");
sst>>obj;
std::cout<<obj;
// use string literal
Svar lit="[false,3]"_svar;
if(s.is<std::string>()) // use is to check type
std::cout<<"raw string is "<<s.as<std::string>(); // use as to force cast, never throw
double d=i.castAs<double>();// use castAs, this may throw SvarException

Use Svar for Argument Parsing

Svar provides argument parsing functional with JSON configuration loading. Here is a small demo shows how to use Svar for argument parsing:

#include <GSLAM/core/Svar.h>
int main(int argc,char** argv)
{
svar.parseMain(argc,argv);
int argInt=svar.arg<int>("i",0,"This is a demo int parameter");
svar.arg<bool>("b",false,"Here is the bool description");
if(svar.get<bool>("help",false)){
svar.help();
return 0;
}
return 0;
}

When you use "--help" or "-help", the terminal should show the below introduction:

-> sample_use --help
Usage:
sample_use [--help] [-conf configure_file] [-arg_name arg_value]...
Using Svar supported argument parsing. The following table listed several argume
nt introductions.
Argument Type(default->setted) Introduction
--------------------------------------------------------------------------------
-i int(0) This is a demo int parameter
-b bool(false) Here is the bool description
-conf str("Default.cfg") The default configure file going
to parse.
-help bool(false->true) Show the help information.

"help" and "conf" is two default parameters and users can use "conf" to load JSON file for configuration loading.

Svar supports the following parsing styles:

Use Svar to Hold Other C++ Elements

Svar can not only hold JSON types, but also any other c++ elements including functions, classes and objects.

A tiny sample of Svar holding functions and classes.

#include <GSLAM/core/Svar.h>
int c_func(int a,int b){return a+b;}
class DemoClass{
public:
DemoClass(const std::string& name):_name(name){}
std::string getName()const{return _name;}
void setName(std::string name){_name=name}
static DemoClass create(std::string name){return DemoClass(name);}
std::string _name;
};
int main(int argc,char** argv)
{
Svar add=c_func;// hold a function
int c=add(a,b).castAs<int>();// use the function
Svar create(&DemoClass::create);// hold a static member function
DemoClass inst=create("hello svar").as<DemoClass>();// call a static member function
Svar(&DemoClass::setName)(&inst,"I changed name");// call a member function
Svar cls=SvarClass::instance<DemoClass>();
Class<DemoClass>()
.construct<const std::string&>()
.def("getName",&DemoClass::getName)
.def("setName",&DemoClass::setName)
.def_static("create",&DemoClass::create)
.def_readwrite("name",&DemoClass::_name);
Svar inst2=cls("I am another instance");// construct a instance
std::string name=inst2.call("getName").as<std::string>();
inst2.call("setName","changed name with svar");
}

Define Class Members with Svar

Svar does not do magic, so users need to bind functions mannually like boost or pybind11 did. Fortunately, Svar provided utils to bind those functions very conveniencely.

As used in last section, there are three types of functions: constructor, member function and static member function. And Svar also support lambda expression of functions so that users able to extend functions more easily.

Class<DemoClass>("Demo") // change the name to Demo
.construct<const std::string&>() // constructor
.def("getName",&DemoClass::getName) // const member function
.def("setName",&DemoClass::setName) // member function
.def_static("create",&DemoClass::create)// static function
.def("print",[](DemoClass& self){std::cerr<<self._name;})// lambda function
.def_static("none",[](){});// static lambda function
Todo:
Svar does not support field now, maybe it's a future work.

Operators of Svar

Svar supports to call functions with operators, such as for int, we got some math operators and we want to use it directly with Svar:

Svar a=4;
Svar b=5;
auto c=a+b;
EXPECT_TRUE(c.is<int>())
EXPECT_EQ(c,20)

The above operation is available because Svar builtin defined as following:

SvarClass::Class<int>()
.def("__init__",&SvarBuiltin::int_create)
.def("__double__",[](int i){return (double)i;})
.def("__bool__",[](int i){return (bool)i;})
.def("__str__",[](int i){return Svar::toString(i);})
.def("__eq__",[](int self,int rh){return self==rh;})
.def("__lt__",[](int self,int rh){return self<rh;})
.def("__add__",[](int self,Svar rh)->Svar{
if(rh.is<int>()) return Svar(self+rh.as<int>());
if(rh.is<double>()) return Svar(self+rh.as<double>());
return Svar::Undefined();
})
.def("__sub__",[](int self,Svar rh)->Svar{
if(rh.is<int>()) return Svar(self-rh.as<int>());
if(rh.is<double>()) return Svar(self-rh.as<double>());
return Svar::Undefined();
})
.def("__mul__",[](int self,Svar rh)->Svar{
if(rh.is<int>()) return Svar(self*rh.as<int>());
if(rh.is<double>()) return Svar(self*rh.as<double>());
return Svar::Undefined();
})
.def("__div__",[](int self,Svar rh){
if(rh.is<int>()) return Svar(self/rh.as<int>());
if(rh.is<double>()) return Svar(self/rh.as<double>());
return Svar::Undefined();
})
.def("__mod__",[](int self,int rh){
return self%rh;
})
.def("__neg__",[](int self){return -self;})
.def("__xor__",[](int self,int rh){return self^rh;})
.def("__or__",[](int self,int rh){return self|rh;})
.def("__and__",[](int self,int rh){return self&rh;});

If you want your own class support these operators, you need to define you own implementations. The following table listed some operators that often used.

Operator Function Name
+ "__add__"
- "__sub__"
* "__mul__"
/ "__div__"
% "__mod__"
& "__and__"
| "__or__"
^ "__xor__"
= "__eq__"
!= "__nq__"
< "__lt__"
> "__gt__"
<= "__le__"
>= "__ge__"
[] "__getitem__"

Inherit of Classes

#include <GSLAM/core/Svar.h>
using namespace GSLAM;
class BBase{
public:
virtual bool isBBase(){return true;}
};
class BaseClass: public BBase{
public:
BaseClass(int age):age_(age){}
int getAge()const{return age_;}
void setAge(int a){age_=a;}
virtual bool isBBase(){return false;}
int age_;
};
int main(int argc,char** argv)
{
Class<BBase>()
.def("isBBase",&BBase::isBBase);
Class<BaseClass>()
.inherit<BBase,BaseClass>() // use this to inherit
.def_static("__init__",[](int age){return BaseClass(age);})// replace construct<int>()
.def("getAge",&BaseClass::getAge)
.def("setAge",&BaseClass::setAge)
.def_readwrite("age",&BaseClass::age_);
Svar cls=SvarClass::instance<BaseClass>();
Svar obj=cls(10);// ten years old?
assert(!obj.call("isBBase).as<bool>());
return 0;
}

Export & Import Svar Module with C++ Shared Library

One of the most important design target of Svar is to export a shared library that can be used by different languages.

Export Svar Module

The only thing needs to do is to put what you want to the singleton of Svar: Svar::instance(), which is defined as 'svar', and expose the symbol with macro EXPORT_SVAR_INSTANCE.

#include "GSLAM/core/Svar.h"
using namespace GSLAM;
int add(int a,int b){
return a+b;
}
class ApplicationDemo{
public:
ApplicationDemo(std::string name):_name(name){
std::cout<<"Application Created.";
}
std::string name()const{return _name;}
std::string gslam_version()const{return "3.2";}
std::string _name;
};
REGISTER_SVAR_MODULE(sample)// see, so easy, haha
{
svar["__name__"]="sample_module";
svar["__doc__"]="This is a demo to show how to export a module using svar.";
svar["add"]=add;
Class<ApplicationDemo>()
.construct<std::string>()
.def("name",&ApplicationDemo::name)
.def("gslam_version",&ApplicationDemo::gslam_version);
svar["ApplicationDemo"]=SvarClass::instance<ApplicationDemo>();
}
EXPORT_SVAR_INSTANCE // export the symbol of Svar::instance

Compile this file to a shared library "libsample.so" or "sample.dll", and we are going to access this module with Svar later.

Documentation of Svar Module

A Svar module is designed to be self maintained, which means the module can be called without header or documentation. Since SvarFunction auto generated function signatures, so that users are able to know how to call Svar functions.

One can use the 'svar' application from https://github.com/zdzhaoyong/Svar, or the "gslam" application of https://github.com/zdzhaoyong/GSLAM to access the context.

-> gslam doc -plugin sample
{
"ApplicationDemo" : <class at 0x15707d0>,
"__builtin__" : {
"Json" : <class at 0x156fd80>,
"version" : 256
},
"__doc__" : "This is a demo to show how to export a module using svar.",
"__name__" : "sample_module",
"add" : <function at 0x15706e0>
}
-> gslam doc -plugin sample -key ApplicationDemo
class ApplicationDemo():
| ApplicationDemo.__init__(...)
| ApplicationDemo.__init__(str)->ApplicationDemo
|
|
| Methods defined here:
| ApplicationDemo.__init__(...)
| ApplicationDemo.__init__(str)->ApplicationDemo
|
|
| ApplicationDemo.gslam_version(...)
| ApplicationDemo.gslam_version(ApplicationDemo const*)->str
|
|
| ApplicationDemo.name(...)
| ApplicationDemo.name(ApplicationDemo const*)->str
|
|

Use Svar Module in C++

We are able to load the Svar instance exported by marco with EXPORT_SVAR_INSTANCE using Registry::load().

#include "GSLAM/core/Svar.h"
#include "GSLAM/core/Registry.h"
using namespace GSLAM;
int main(int argc,char** argv){
Svar sampleModule=Registry::load("sample");
Svar ApplicationDemo=sampleModule["ApplicationDemo"];
Svar instance=ApplicationDemo("zhaoyong");
std::cout<<instance.call("gslam_version");
std::cout<<ApplicationDemo.as<SvarClass>();
return 0;
}