2

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

sehe
  • 374,641
  • 47
  • 450
  • 633
Kargath
  • 450
  • 5
  • 15

1 Answers1

2

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.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • >The arguments are required to be passed as a JSON array How did you know this, I looked up the documentation related to boost::json::value, but didn't find this, I also tried to find out by reading the code implementation of boost::json::value, Neither was found. Also, can you explain what` class R `means here? – Kargath May 27 '22 at 09:55
  • I figured it out, R is the return value, and C2::* is the pointer to the member function, right? – Kargath May 27 '22 at 10:14
  • "How did you know this" - I just looked at the sample code you linked. E.g. ["call_impl( c, D.pointer, args.as_array() )"](https://www.boost.org/doc/libs/develop/libs/describe/doc/html/describe.html#:~:text=call_impl(%20c%2C%20D.pointer%2C%20args.as_array()%20)) treats that value as an array. That has nothing to do with Boost JSON documentaion, because Boost JSON doesn't have that requirement, it's general purpose. – sehe May 27 '22 at 14:40
  • Incidentally, `{1, 2}` and `{"World"}` create arrays as well - which you *can* see in the Boost JSON docs: ["Simple initializer lists produce an array"](https://www.boost.org/doc/libs/1_79_0/libs/json/doc/html/json/dom/initializer_lists.html) – sehe May 27 '22 at 14:41