I have a collection of C++ functions that all accept a type from the same set of types (TypeA, TypeB and TypeC in the example below) as template parameter. To ease the exposition of these functions to python, I would like to define, for each of them, a function that takes the type not as template parameter, but as a string argument, as shown below:
template<typename dataType>
int function(int arg)
{
...
}
int function(int arg, string type)
{
if (type == "type_A")
{
return function<TypeA>(arg);
}
else if (type == "type_B")
{
return function<TypeB>(arg);
}
else if (type == "type_C")
{
return function<TypeC>(arg);
}
else
{
std::cerr << "Invalid type!" << std::endl;
exit(1);
}
}
At the moment, I wrap all the functions in this way, but this leads to a lot of code repetition, so I was wondering whether there was a better way to do this, perhaps using preprocessor directives?
One way to reduce the if/else logic is to store a map of std::function objects and use the map to make the right call.
int function(int arg, std::string type)
{
using FMap = std::map<std::string, std::function<int(int)>>;
static const FMap fmap{{"type_A", [](int arg) { return function<TypeA>(arg); }},
{"type_B", [](int arg) { return function<TypeB>(arg); }},
{"type_C", [](int arg) { return function<TypeC>(arg); }}};
auto iter = fmap.find(type);
if ( iter != fmap.end() )
{
return iter->second(arg);
}
std::cerr << "Invalid type!" << std::endl;
exit(1);
return 0;
}
If you are willing to rename the function template, you can simplify the code for constructing the function map.
template <typename T>
int fun_2(int arg) { ... }
int function(int arg, std::string type)
{
using FMap = std::map<std::string, std::function<int(int)>>;
static const FMap fmap{{"type_A", fun_2<TypeA>},
{"type_B", fun_2<TypeB>},
{"type_C", fun_2<TypeC>}};
auto iter = fmap.find(type);
if ( iter != fmap.end() )
{
return iter->second(arg);
}
std::cerr << "Invalid type!" << std::endl;
exit(1);
return 0;
}
Related
I have some var = std::variant<std::monostate, a, b, c> when a, b, c is some types.
How, at runtime, do I check what type var contains?
In the official documentation I found information that if var contains a type and I write std::get<b>(var) I get an exception. So I thought about this solution:
try {
std::variant<a>(var);
// Do something
} catch(const std::bad_variant_access&) {
try {
std::variant<b>(var);
// Do something else
} catch(const std::bad_variant_access&) {
try {
std::variant<c>(var);
// Another else
} catch (const std::bad_variant_access&) {
// std::monostate
}
}
}
But it's so complicated and ugly! Is there a simpler way to check what type std::variant contains?
std::visit is the way to go:
There is even overloaded to allow inlined visitor:
// helper type for the visitor #4
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
and so:
std::visit(overloaded{
[](std::monostate&){/*..*/},
[](a&){/*..*/},
[](b&){/*..*/},
[](c&){/*..*/}
}, var);
To use chained if-branches instead, you might used std::get_if
if (auto* v = std::get_if<a>(var)) {
// ...
} else if (auto* v = std::get_if<b>(var)) {
// ...
} else if (auto* v = std::get_if<c>(var)) {
// ...
} else { // std::monostate
// ...
}
The most simple way is to switch based on the current std::variant::index(). This approach requires your types (std::monostate, A, B, C) to always stay in the same order.
// I omitted C to keep the example simpler, the principle is the same
using my_variant = std::variant<std::monostate, A, B>;
void foo(my_variant &v) {
switch (v.index()) {
case 0: break; // do nothing because the type is std::monostate
case 1: {
doSomethingWith(std::get<A>(v));
break;
}
case 2: {
doSomethingElseWith(std::get<B>(v));
break;
}
}
}
If your callable works with any type, you can also use std::visit:
void bar(my_variant &v) {
std::visit([](auto &&arg) -> void {
// Here, arg is std::monostate, A or B
// This lambda needs to compile with all three options.
// The lambda returns void because we don't modify the variant, so
// we could also use const& arg.
}, v);
}
If you don't want std::visit to accept std::monostate, then just check if the index is 0. Once again, this relies on std::monostate being the first type of the variant, so it is good practice to always make it the first.
You can also detect the type using if-constexpr inside the callable. With this approach, the arguments don't have to be in the same order anymore:
void bar(my_variant &v) {
std::visit([](auto &&arg) -> my_variant {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<std::monostate, T>) {
return arg; // arg is std::monostate here
}
else if constexpr (std::is_same_v<A, T>) {
return arg + arg; // arg is A here
}
else if constexpr (std::is_same_v<B, T>) {
return arg * arg; // arg is B here
}
}, v);
}
Note that the first lambda returns void because it just processes the current value of the variant. If you want to modify the variant, your lambda needs to return my_variant again.
You could use an overloaded visitor inside std::visit to handle A or B separately. See std::visit for more examples.
You can use standard std::visit
Usage example:
#include <variant>
#include <iostream>
#include <type_traits>
struct a {};
struct b {};
struct c {};
int main()
{
std::variant<a, b, c> var = a{};
std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, a>)
std::cout << "is an a" << '\n';
else if constexpr (std::is_same_v<T, b>)
std::cout << "is a b" << '\n';
else if constexpr (std::is_same_v<T, c>)
std::cout << "is a c" << '\n';
else
std::cout << "is not in variant type list" << '\n';
}, var);
}
Well, with some macro magic, you can do something like:
#include <variant>
#include <type_traits>
#include <iostream>
#define __X_CONCAT_1(x,y) x ## y
#define __X_CONCAT(x,y) __X_CONCAT_1(x,y)
template <typename T>
struct __helper { };
// extract the type from a declaration
// we use function-type magic to get that: typename __helper<void ( (declaration) )>::type
// declaration is "int &x" for example, this class template extracts "int"
template <typename T>
struct __helper<void (T)> {
using type = std::remove_reference_t<T>;
};
#define variant_if(variant, declaration) \
if (bool __X_CONCAT(variant_if_bool_, __LINE__) = true; auto * __X_CONCAT(variant_if_ptr_, __LINE__) = std::get_if<typename __helper<void ( (declaration) )>::type>(&(variant))) \
for (declaration = * __X_CONCAT(variant_if_ptr_, __LINE__); __X_CONCAT(variant_if_bool_, __LINE__); __X_CONCAT(variant_if_bool_, __LINE__) = false)
#define variant_switch(variant) if (auto &__variant_switch_v = (variant); true)
#define variant_case(x) variant_if(__variant_switch_v, x)
int main() {
std::variant<int, long> v = 12;
std::variant<int, long> w = 32l;
std::cout << "variant_if test" << std::endl;
variant_if(v, int &x) {
std::cout << "int = " << x << std::endl;
}
else variant_if(v, long &x) {
std::cout << "long = " << x << std::endl;
}
std::cout << "variant_switch test" << std::endl;
variant_switch(v) {
variant_case(int &x) {
std::cout << "int = " << x << std::endl;
variant_switch (w) {
variant_case(int &x) {
std::cout << "int = " << x << std::endl;
}
variant_case(long &x) {
std::cout << "long = " << x << std::endl;
}
}
};
variant_case(long &x) {
std::cout << "long = " << x << std::endl;
variant_switch (w) {
variant_case(int &x) {
std::cout << "int = " << x << std::endl;
}
variant_case(long &x) {
std::cout << "long = " << x << std::endl;
}
}
};
}
return 0;
}
I tested this approach with GCC and Clang, no guarantees for MSVC.
I am working on a program, that has to initialize many different objects according to a list that defines which type each object is.
The code that does this task looks like this:
// name is one entry of the type list
// itemList is a std::vector where the new items are appended
if(name == "foo")
{
initItem<FooObject>(itemList);
}
else if(name == "bar")
{
initItem<BarObject>(itemList);
}
else if(name == "baz")
{
initItem<Object3>(itemList);
}
....
initItem(ItemList) allocates an object of type T and appends it to itemList.
At other place in the code there are similar conditional statements for the different object types.
At the moment for each new object type added I have to add a new else if to all the conditional statements which is kind of annoying.
Is there a way to just define some kind of map somewhere that holds the assignment like
"foo", FooObject,
"bar", BarObject,
"baz", Object3,
and then template/auto-generate (maybe by preprocessor) the if-else statements so i don't have to setup them by hand every time?
Edit: Here is the whole method that contains the code snipset (there are many more else if() statements that all work according to the same principal.
bool Model::xml2Tree(const pugi::xml_node &xml_node, std::vector<TreeItem*> &parents)
{
bool all_ok = true;
bool sucess;
pugi::xml_node child_node = xml_node.first_child();
for (; child_node; child_node = child_node.next_sibling())
{
sucess = true;
bool ok = false;
std::string name = child_node.name();
if(name == "foo")
{
ok = initTreeItem<FooObject>(child_node, parents);
}
else if(name == "bar")
{
ok = initTreeItem<BarObject>(child_node, parents);
}
...
...
...
else
{
ok = false;
std::cout << "Unknown Element" << std::endl;
continue;
}
if(!sucess)
{
continue;
}
all_ok = all_ok && ok;
// recursiv
ok = xml2Tree(child_node, parents);
all_ok = all_ok && ok;
}
parents.pop_back();
return all_ok;
}
template<class T>
bool Model::initTreeItem(const pugi::xml_node &xml_node,
std::vector<TreeItem *> &parents)
{
bool ok = false;
T *pos = new T(parents.back());
parents.back()->appendChild(pos);
ok = pos->initFromXml(xml_node);
parents.push_back(pos);
return ok;
}
Firstly, you can encode your mapping in the type system as follows:
template <typename T>
struct type_wrapper { using type = T; };
template <typename T>
inline constexpr type_wrapper<T> t{};
template <typename K, typename V>
struct pair
{
K _k;
V _v;
constexpr pair(K k, V v) : _k{k}, _v{v} { }
};
template <typename... Ts>
struct map : Ts...
{
constexpr map(Ts... xs) : Ts{xs}... { }
};
constexpr auto my_map = map{
pair{[]{ return "foo"; }, t<FooObject>},
pair{[]{ return "bar"; }, t<BarObject>},
pair{[]{ return "baz"; }, t<Object3>}
};
We're using lambdas as they're implicitly constexpr in C++17, in order to simulate "constexpr arguments". If you do not require this, you can create a constexpr wrapper over a string literal and use that instead.
You can then go through the mapping with something like this:
template <typename... Pairs>
void for_kv_pairs(const std::string& name, map<Pairs...> m)
{
([&]<typename K, typename V>(const pair<K, V>& p)
{
if(name == p._k())
{
initItem<typename V::type>();
}
}(static_cast<const Pairs&>(m)), ...);
}
This is using a fold expression over the comma operator plus C++20 template syntax in lambdas. The latter can be replaced by providing an extra implementation function to retrieve K and V from pair pre-C++20.
Usage:
template <typename X>
void initItem()
{
std::cout << typeid(X).name() << '\n';
}
struct FooObject { };
struct BarObject { };
struct Object3 { };
constexpr auto my_map = map{
pair{[]{ return "foo"; }, t<FooObject>},
pair{[]{ return "bar"; }, t<BarObject>},
pair{[]{ return "baz"; }, t<Object3>}
};
int main()
{
for_kv_pairs("bar", my_map);
}
Output:
9BarObject
live example on wandbox.org
You can use higher-order macros (or x-macros) to generate code like that, for example:
#define each_item(item, sep) \
item("foo", FooObject) sep \
item("bar", BarObject) sep \
item("baz", Object3)
#define item_init(item_name, item_type) \
if (name == item_name) { \
initItem<item_type>(itemList); \
}
each_item(item_init, else)
I'm new to Lua and LuaBridge and I'm trying to figure out if it's possible to register a templated function? I've looked online and through the LuaBridge manual to no avail. What I tried was creating a pointer to the base class, but then found out later that there's no way to cast in Lua. If anyone has any ideas on the best way to resolve this issue it would be appreciated.
template<typename T>
T* GetComponentByType()
{
try
{
for (ComponentVectorWrapper::t_Component_Iter iter = m_Components_.begin(); iter != m_Components_.end(); ++iter)
if (*iter != nullptr)
if (T* type = dynamic_cast<T*>(*iter))
return type;
throw ComponentMissingException();
}
catch (ComponentMissingException& e)
{
std::cout << e.what() << std::endl;
__debugbreak();
}
}
Component* getComponentByType(std::string type)
{
if (type == "Transform")
return GetComponentByType<TransformComponent>();
return nullptr;
}
static void registerLua(lua_State* L)
{
using namespace luabridge;
getGlobalNamespace(L)
.beginClass<GameObject>("GameObject")
.addConstructor<void(*)(const char* name)>()
.addData<const char*>("name", &GameObject::m_Name_, false)
.addData<TransformComponent*>("transform", &GameObject::m_Transform)
.addFunction("addComponent", &GameObject::registerComponent)
.addFunction("getComponent", &GameObject::getComponentByType)
.addFunction("removeComponent", &GameObject::removeComponent)
.endClass();
}
Solution
Forgot to post this earlier but the solution is to determine the type from a string, from there you need to set a global in Lua and then return a reference to that global.
luabridge::LuaRef GameObject::luaGetComponent(std::string type)
{
// Return component
return luaGetComponentHelper(type, false, "");
}
luabridge::LuaRef GameObject::luaGetComponentHelper(std::string type, bool findAll, const char* tag)
{
lua_State* L = (&LuaEngine::getInstance())->L();
// Find component type
if (type == "TransformComponent")
LuaHelper::GetGlobalComponent<TransformComponent>(*this, findAll, m_CompName, tag);
else if (type == "CameraComponent")
LuaHelper::GetGlobalComponent<CameraComponent>(*this, findAll, m_CompName, tag);
else if (type == "FirstPersonCameraComponent")
LuaHelper::GetGlobalComponent<FirstPersonCameraComponent>(*this, findAll, m_CompName, tag);
else if (type == "RenderComponent")
LuaHelper::GetGlobalComponent<RenderComponent>(*this, findAll, m_CompName, tag);
else if (type == "ThirdPersonCameraComponent")
LuaHelper::GetGlobalComponent<ThirdPersonCameraComponent>(*this, findAll, m_CompName, tag);
else if (type == "CanvasComponent")
LuaHelper::GetGlobalComponent<CanvasComponent>(*this, findAll, m_CompName, tag);
else if (type == "RigidBody")
LuaHelper::GetGlobalComponent<RigidBody>(*this, findAll, m_CompName, tag);
else if (type == "BoxCollider")
LuaHelper::GetGlobalComponent<BoxCollider>(*this, findAll, m_CompName, tag);
else
{
luabridge::setGlobal(L, nullptr, m_CompName); // Prevents errors
LuaEngine::printError("Component not found.");
}
// Return component
return luabridge::getGlobal(L, m_CompName);
}
template<typename T>
static luabridge::LuaRef GetGlobalComponent(GameObject& go, bool findAll, const char* globalName, const char* tag)
{
// Get lua state
auto L = LuaEngine::getInstance().L();
// Register global
if (findAll)
{
auto vec = go.GetComponentsByType<T>();
// Check for tag
if (tag != "")
{
// Find by tag
std::vector<T*> elements;
for (auto& e : vec)
{
if (static_cast<Component*>(e)->getTag() == tag)
elements.push_back(e);
}
luabridge::setGlobal(L, LuaHelper::ToTable(elements), globalName);
}
else
luabridge::setGlobal(L, LuaHelper::ToTable(vec), globalName);
}
else
luabridge::setGlobal(L, go.GetComponentByType<T>(), globalName);
return luabridge::getGlobal(L, globalName);
}
You cannot register the templated function. You have to register explicit instantiations.
#include <iostream>
#include <lua.hpp>
#include <LuaBridge.h>
char const script [] =
"local t = Test()"
"t:test_int(123)"
"t:test_str('Hello')";
class Test
{
public:
template < typename T >
void test(T t) { std::cout << t << '\n'; }
};
int main()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
luabridge::getGlobalNamespace(L)
.beginClass<Test>("Test")
.addConstructor<void(*)(void)>()
.addFunction("test_int", &Test::test<int>)
.addFunction("test_str", &Test::test<char const *>)
.endClass();
if ( luaL_dostring(L, script) != 0)
std::cerr << lua_tostring(L,-1) << '\n';
}
I'd suggest you use sol2 which does not have such an awful syntax (needs C++14 though).
#include <iostream>
#include <string>
#include <sol.hpp>
char const script [] =
"local t = Test.new()"
"t:test_int(123)"
"t:test_str('Hello')";
class Test
{
public:
template < typename T >
void test(T t) { std::cout << t << '\n'; }
};
int main()
{
sol::state L;
L.open_libraries();
L.new_usertype<Test>("Test",
"test_int", &Test::test<int>,
"test_str", &Test::test<std::string>
);
L.script(script);
}
I want to write one function which would return me String, Int, Float based on some criteria. Using C11. Tried couple of stuff already mentioned, doesn't work with template/auto. If the function has only one return then its fine at the moment. When I add another return say string, compilation error. error: invalid conversion from int to char*
template<typename T>
T getProperty(int field,Json::Value root)
{
for(int i =0;i < root["properties"].size(); i++)
{
if(root["properties"][i]["field"].asInt() == field)
{
if(strcmp(root["properties"][i]["type"].asString().c_str(),"int") == 0)
{
T convertedValue = (root["properties"][i]["currVal"].asInt());
return convertedValue;
}
if(strcmp(root["properties"][i]["type"].asString().c_str(),"string") == 0)
{
T convertedValue;
sprintf(convertedValue,"%s",root["properties"][i]["currVal"].asString().c_str());
return convertedValue;
}
}
}
}
If you're using C++14, this simple example does work
template <typename T>
auto test(T a){
return a;
}
int main()
{
int a = 1;
std::cout << test(a) << std::endl;
double b = 2.0;
std::cout << test(b) << std::endl;
std::string s {"ss"};
std::cout << test(s) << std::endl;
}
of course you will need to know the type you want to retrieve before the call, and that's probably not what you're trying to achieve
You could use boost::variant<string, float, int> / boost::any as return type.
Please note that these classes are part of C++17 standard library.
You can do it by fully specialising your template. But you should probably split finding the correct field with returning the value.
You don't have to throw these exceptions, but if you don't, on the error path your program has undefined behaviour.
Json::Value getField(int field,Json::Value root)
{
auto it = std::find_if(root["properties"].begin(), root["properties"].end(),
[field](Json::Value item){ return item["field"].toInt() == field });
if (it = root["properties"].end())
{ throw std::runtime_error("missing field"); }
return *it;
}
// Primary template defined but not declared
template<typename T> T getProperty(int field,Json::Value root);
template<> int getProperty(int field,Json::Value root)
{
Json::Value value = getField(field, root);
if (value["type"].asString() == "int")
{ throw std::runtime_error("not an int"); }
return value["currVal"].asInt();
}
template<> std::string getProperty(int field,Json::Value root)
{
Json::Value value = getField(field, root);
if (value["type"].asString() == "string")
{ throw std::runtime_error("not a string"); }
return value["currVal"].asString();
}
// etc
Can someone give me idea on this problem. I have searched on internet about this, but couldn't get much info as I wished to have.
Say there is a class.
class Foo {
explicit Foo() {}
int getVar1();
int getVar2();
void setVar1(int v);
void setVar2(int v);
private:
int var1, var2;
};
now given a list of tokens {"var1", "var2", ... "varN"}, is there any way I can create the function name at runtime and call those member functions of some object of type Foo. like for e.g
Foo obj;
string input = "Var1,Var2,Var3,...VarN";
vector<string> tokens = splitString(input);
for (vector<string>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) {
string funName = "get" + *it;
// somehow call obj.getVar1()....obj.getVarN()
}
using if else is fine for small numbers of variables, but its not good for large number of variables. Using bind and functors also doesn't solve this. One webpage suggested making memory executable at runtime and then using reinterpret_cast, I don't know whether this would work.
UPDATE
Ok, as from the answers and other searches on internet, I see that there is not elegant way of doing this in C++. There is no reflection in C++ as of now. All hacks would require compile time resolution of member function pointers.
Could someone give me ideas on alternate class design in these scenario when you have lots of variables and setters and getters functions...or whether getters and setters are good practice in c++ ?
As an idea consider the following code
struct A
{
void f1() { std::cout << "A::f1()\n"; }
void f2() { std::cout << "A::f2()\n"; }
void f3() { std::cout << "A::f3()\n"; }
void f4() { std::cout << "A::f4()\n"; }
};
std::map<std::string, void( A::* )()> m = { { "f1", &A::f1 }, { "f2", &A::f2 }, { "f3", &A::f3 }, { "f4", &A::f4 } };
A a;
for ( auto p : m ) ( a.*p.second )();
You can make the map as a data member of your class.
You can't "add" members at runtime. C++ is strongly typed at compile time.
You can get the behaviour you want by having a map<string, func_type> and using it to resolve your string to an actual function. You can create it using macros to make sure that the string names match the function names.
#DEFINE ADD_METHOD(map_var, func) map_var["func"] = &func
A simple/not perfect solution could be to use a intermediate methods checking the parameter and calling the getVar* method accordingly.
An example like this one maybe:
class Foo
{
public:
explicit Foo() {}
int getVar1() { return 1; }
int getVar2() { return 2; }
void setVar1(int v) { var1 = v; }
void setVar2(int v) { var2 = v; }
int callGetVar(const std::string &var)
{
if (var == "Var1") return getVar1();
if (var == "Var2") return getVar2();
else { return -1; }
}
private:
int var1, var2;
};
int main()
{
Foo obj;
std::string input = "Var1,Var2,Var3,...VarN";
std::vector<std::string> tokens = { "Var1", "Var2", "Var2", "Var1", "Var1", "Var2", "Var2", "Var1"};
auto tokensIT = tokens.begin();
for (; tokensIT != tokens.end(); ++tokensIT)
{
// somehow call obj.getVar1()....obj.getVarN()
std::cout << obj.callGetVar(*tokensIT);
}
return 0;
}
why not look at it in a referent way:
For each variable assign an index number, starting from 0, 1, 2....
You keep this values in a map (key is the variable name, value is the assigned value).
All the values of those variables, you keep in an array, so that the value of the first variable in in cell 0, the next one is in cell 1 etc.
so, when you want to get/set value, all you need to do, is, find it's index in the map, and access the relevant cell in vector.
You can try this
one example:
template<class C1, class C2, class R, class... A, std::size_t... I>
boost::json::value
call_impl_(C1& c1, R(C2::* pmf)(A...), boost::json::array const& args,
std::index_sequence<I...>)
{
return boost::json::value_from(
(c1.*pmf)(boost::json::value_to< boost::remove_cv_ref_t<A> >(args[I])...));
}
template<class C1, class C2, class R, class... A>
boost::json::value
call_impl(C1& c1, R(C2::* pmf)(A...), boost::json::array const& args)
{
if (args.size() != sizeof...(A))
{
throw std::invalid_argument("Invalid number of arguments");
}
return call_impl_(c1, pmf, args, std::index_sequence_for<A...>());
}
template<class C>
boost::json::value
call(C& c, boost::string_view method, boost::json::value const& args)
{
using Fd = boost::describe::describe_members<C,
boost::describe::mod_public | boost::describe::mod_function>;
bool found = false;
boost::json::value result;
boost::mp11::mp_for_each<Fd>([&](auto D) {
if (!found && method == D.name)
{
result = call_impl(c, D.pointer, args.as_array());
found = true;
}
});
if (!found)
{
throw std::invalid_argument("Invalid method name");
}
return result;
}
//test1 from https://github.com/bytemaster/boost_reflect
struct calculator { //need Generic maybe..
int add(int v, int u) { return u + v; }
int sub(int v) { return result_ -= v; }
int result() { return result_; }
private:
int result_ = 0.0;
};
BOOST_DESCRIBE_STRUCT(calculator, (), (add, sub), (result));
int main(int argc, char** argv) {
calculator cal;
std::string line;
std::string cmd;
std::string args;
while (true) {
std::cerr << "Enter Method: ";
std::getline(std::cin, line);
int pos = line.find('(');
cmd = line.substr(0, pos);
args = line.substr(pos + 1, line.size() - pos - 2);
std::cout << "args: " << args << std::endl;
std::vector<std::string> num_str;
boost::split(num_str, args, boost::is_any_of(","));
std::vector<int> nums;
std::for_each(num_str.begin(), num_str.end(), [&](std::string str) {nums.push_back(std::stoi(str)); });
// Convert the vector to a JSON array
const boost::json::value jv = boost::json::value_from(nums);
std::cout << call(cal, cmd, jv) << std::endl;
}
return 0;
}
It can be passed under visual studio 2022 c++17.
with cpp20 it will report an error, I don’t know why