Home > Software design >  How to call a parameterless function using boost::json::value
How to call a parameterless function using boost::json::value

Time:05-27

I'm modifying from here code:

#include <boost/describe.hpp>
#include <boost/mp11.hpp>
#include <boost/json.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/string_view.hpp>
#include <stdexcept>
#include <string>
#include<iostream>
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;
}

struct Object
{
  std::string greet(std::string const& who)
  {
    return "Hello, "   who   "!";
  }

  int add(int x, int y)
  {
    return x   y;
  }
  int foobar()
  {
    std::cout << "I'm stupid!" << std::endl;
    return 1;
  }
};

BOOST_DESCRIBE_STRUCT(Object, (), (greet, add, foobar))

int main()
{
  Object obj;
  std::cout << call(obj, "greet", { "world" }) << std::endl;
  std::cout << call(obj, "add", { 1, 2 }) << std::endl;
  boost::json::value sc{};
  std::cout << call(obj, "add", sc) << std::endl;
}

The part I added is

int foobar()
{
    std::cout << "I'm stupid!" << std::endl;
    return 1;
}

and

boost::json::value sc{};
std::cout << call(obj, "foobar", sc) << std::endl;

output:

error C2672: 'call_impl': no matching overloaded function found

I know I can add an overload of call_impl and modify call to:

  boost::mp11::mp_for_each<Fd>([&](auto D) {
    if (!found && method == D.name)
    {
      auto temp = args.as_array();
      std::cout << typeid(temp).name() << std::endl;
      if (!temp.empty())
      result = call_impl(c, D.pointer,temp );
      else
        result = call_impl(c, D.pointer);
      found = true;
    }});

my question here is:

  1. how to construct a boost::json::value that contains empty values,so I can use it to call that function.

I think I've described my problem clearly, so many of my attempts and manipulations are so wrong that they don't need to be written, but it still warns me "It looks like your post is mostly code; please add some more details." ...

OS:windows11
IDE:visual studio2019

CodePudding user response:

The arguments are required to be passed as a JSON array. The simplest way to to fullfill the requirement is:

std::cout << call(obj, "foobar", boost::json::array{}) << std::endl;

See it Live On Coliru

#include <boost/describe.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for Coliru
#include <boost/mp11.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/string_view.hpp>
#include <iostream>
#include <stdexcept>
#include <string>

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;
}

struct Object {
    std::string greet(std::string const& who) {
        return "Hello, "   who   "!";
    }

    int foobar() {
        std::cout << "I'm not so stupid after all!" << std::endl;
        return 42;
    }

    int add(int x, int y) {
        return x   y;
    }
};

BOOST_DESCRIBE_STRUCT(Object, (), (greet, add, foobar))

#include <iostream>

int main() {
    Object obj;
    std::cout << call(obj, "greet", {"world"}) << std::endl;
    std::cout << call(obj, "add", {1, 2}) << std::endl;
    std::cout << call(obj, "foobar", boost::json::array{}) << std::endl;
}

Prints

"Hello, world!"
3
I'm not so stupid after all!
42

Of course you can make that the default argument value: Live Demo but in reality, you would only use this in generic code, where you don't know in advance that a function has no parameters. So I don't think that adds any value.

  • Related