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:
- A dynamic link library that can be used as a module in languages such as C++, Python, and Node-JS;
- Unified C++ library interface, which can be called as a plug-in module. The released library comes with documentation, making *.h header file interface description unnecessary;
- Dynamic features. It wraps variables, functions, and classes into Svar while maintaining efficiency;
- Built-in Json support, parameter configuration parsing, thread-safe reading and writing, data decoupling sharing between modules, etc.
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;
Svar lit="[false,3]"_svar;
if(s.is<std::string>())
std::cout<<"raw string is "<<s.as<std::string>();
double d=i.castAs<double>();
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:
- "-arg value": two '-' such as "--arg value" is the same
- "-arg=value": two "--" such as "--arg=value" is the same
- "arg=value"
- "-arg" : this is the same with "arg=true", but the next argument should not be a value
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;
int c=add(a,b).castAs<int>();
Svar create(&DemoClass::create);
DemoClass inst=create("hello svar").as<DemoClass>();
Svar(&DemoClass::setName)(&inst,"I changed name");
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");
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")
.construct<const std::string&>()
.def("getName",&DemoClass::getName)
.def("setName",&DemoClass::setName)
.def_static("create",&DemoClass::create)
.def("print",[](DemoClass& self){std::cerr<<self._name;})
.def_static("none",[](){});
- 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>
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>()
.def_static("__init__",[](int age){return BaseClass(age);})
.def("getAge",&BaseClass::getAge)
.def("setAge",&BaseClass::setAge)
.def_readwrite("age",&BaseClass::age_);
Svar cls=SvarClass::instance<BaseClass>();
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"
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)
{
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
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"
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;
}