1

I am implementing a program,It can make a function call based on the given string.
The implementation is as follows:
it might be a bit long, here is the online version

#include<iostream>
#include<vector>
#include<string>
#include<stack>
#include <boost/describe.hpp>
#include <boost/mp11.hpp>
#include <boost/json.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/string_view.hpp>
using namespace std;
using namespace boost::json;

// C1:class name
// C2:pointer to member function
// R:return type of member function
// A: member function parameter pack
// I: Parameter pack expansion
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])...));
}
// C1: class name
// C2: pointer to member function
// R : return type of member function
// A : member function parameter pack
// args: member function args
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...>());
}
// c:class name
// method: member function name
// args: member function args
// 
template<class C>
boost::json::value call(C& c, boost::string_view method, boost::json::value const& args = boost::json::array{})
{
  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;
}
//    BOOST_DESCRIBE_CLASS(Solution, (), (member_func1, member_func2, member_func3), (), ());

class StackOfPlates {
  vector<stack<int>> store;
  int capacity;
public:
  StackOfPlates(int cap) {
    capacity = cap;
  }
  void push(int val) {
    if (capacity == 0)return;
    if (store.empty() || store.back().size() == capacity)
      store.emplace_back(stack<int>());
    store.back().push(val);
  }
  int pop() {
    if (capacity == 0 || store.empty())
      return -1;
    int res = store.back().top();
    store.back().pop();
    if (store.back().empty())
      store.pop_back();
    return res;
  }
  int popAtStack(int index) {
    if (capacity == 0 || index >= store.size() || store[index].empty())return -1;
    int res = store[index].top();
    store[index].pop();
    if (store[index].empty()) {
      store.erase(store.begin() + index);
    }
    return res;
  }
  BOOST_DESCRIBE_CLASS(StackOfPlates, (), (push, pop, popAtStack), (), ())
};

int main()
{
  vector<std::string> t1{ "push", "push", "popAt", "pop", "pop" };
  vector<vector<int>> t2{ { 1},{2},{1},{},{} };
  StackOfPlates obj(1);// (init1, init2);
  for (int i = 0; i < t1.size(); ++i)
  {
    std::string sa = t1[i];
    std::vector<int> sb = t2[i];
    boost::json::value sc = boost::json::value_from(sb);
    std::cout << call(obj, sa, sc) << std::endl;
  }
  return 0;
}

The library that needs to be installed is boost 1.80
Thecall_impl relevant code comes from the boost documentation
development environment is windows11/ Visual Studio 2022 (v143)
The error message is:

Severity    Code    Description Project File    Line    Suppression State
Error   C2672   'value_from': no matching overloaded function found Project1    C:\Users\mingy\source\repos\Project1\Project1\main.cpp  26  

output:

>main.cpp
1>C:\Users\mingy\source\repos\Project1\Project1\main.cpp(26,23): error C2672: 'value_from': no matching overloaded function found
1>C:\dev\vcpkg\installed\x64-windows\include\boost\json\value_from.hpp(125,1): message : could be 'void boost::json::value_from(T &&,boost::json::value &)'
1>C:\Users\mingy\source\repos\Project1\Project1\main.cpp(26,23): message : 'void boost::json::value_from(T &&,boost::json::value &)': expects 2 arguments - 1 provided
1>C:\dev\vcpkg\installed\x64-windows\include\boost\json\value_from.hpp(125): message : see declaration of 'boost::json::value_from'
1>C:\dev\vcpkg\installed\x64-windows\include\boost\json\value_from.hpp(83,1): message : or       'boost::json::value boost::json::value_from(T &&,boost::json::storage_ptr)'
1>C:\Users\mingy\source\repos\Project1\Project1\main.cpp(26,23): message : Failed to specialize function template 'boost::json::value boost::json::value_from(T &&,boost::json::storage_ptr)'
1>C:\dev\vcpkg\installed\x64-windows\include\boost\json\value_from.hpp(83): message : see declaration of 'boost::json::value_from'
1>C:\Users\mingy\source\repos\Project1\Project1\main.cpp(25,1): message : With the following template arguments:
1>C:\Users\mingy\source\repos\Project1\Project1\main.cpp(25,1): message : 'T=R'
1>C:\Users\mingy\source\repos\Project1\Project1\main.cpp(36): message : see reference to function template instantiation 'boost::json::value call_impl_<C1,StackOfPlates,R,int,0>(C1 &,R (__cdecl StackOfPlates::* )(int),const boost::json::array &,std::integer_sequence<size_t,0>)' being compiled
1>        with
1>        [
1>            C1=StackOfPlates,
1>            R=void
1>        ]
1>C:\Users\mingy\source\repos\Project1\Project1\main.cpp(47): message : see reference to function template instantiation 'boost::json::value call_impl<C,StackOfPlates,void,int>(C1 &,R (__cdecl StackOfPlates::* )(int),const boost::json::array &)' being compiled
1>        with
1>        [
1>            C=StackOfPlates,
1>            C1=StackOfPlates,
1>            R=void
1>        ]
1>C:\dev\vcpkg\installed\x64-windows\include\boost\mp11\algorithm.hpp(1037): message : see reference to function template instantiation 'void call::<lambda_1>::operator ()<boost::describe::detail::member_descriptor<boost_public_member_descriptor_fn::<lambda_1>::()::_boost_desc,1>>(_T1) const' being compiled
1>        with
1>        [
1>            _T1=boost::describe::detail::member_descriptor<boost_public_member_descriptor_fn::<lambda_1>::()::_boost_desc,1>
1>        ]
1>C:\dev\vcpkg\installed\x64-windows\include\boost\mp11\algorithm.hpp(1054): message : see reference to function template instantiation 'F boost::mp11::detail::mp_for_each_impl<boost::describe::detail::member_descriptor<boost_public_member_descriptor_fn::<lambda_1>::()::_boost_desc,1>,boost::describe::detail::member_descriptor<boost_public_member_descriptor_fn::<lambda_2>::()::_boost_desc,1>,boost::describe::detail::member_descriptor<boost_public_member_descriptor_fn::<lambda_3>::()::_boost_desc,1>,_Ty>(boost::mp11::mp_list<boost::describe::detail::member_descriptor<boost_public_member_descriptor_fn::<lambda_1>::()::_boost_desc,1>,boost::describe::detail::member_descriptor<boost_public_member_descriptor_fn::<lambda_2>::()::_boost_desc,1>,boost::describe::detail::member_descriptor<boost_public_member_descriptor_fn::<lambda_3>::()::_boost_desc,1>>,F &&)' being compiled
1>        with
1>        [
1>            F=call::<lambda_1>,
1>            _Ty=call::<lambda_1>
1>        ]
1>C:\Users\mingy\source\repos\Project1\Project1\main.cpp(47): message : see reference to function template instantiation 'call::<lambda_1> boost::mp11::mp_for_each<Fd,call::<lambda_1>>(F &&)' being compiled
1>        with
1>        [
1>            F=call::<lambda_1>
1>        ]
1>C:\Users\mingy\source\repos\Project1\Project1\main.cpp(116): message : see reference to function template instantiation 'boost::json::value call<StackOfPlates>(C &,boost::string_view,const boost::json::value &)' being compiled
1>        with
1>        [
1>            C=StackOfPlates
1>        ]
1>Done building project "Project1.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

When I was using boost1.78 it worked fine, today I reinstalled boost 1.80 and got this kind of error. I'm not very familiar with mp11, so I can't understand it.
Can someone explain what the problem is and how can I fix it?

I once asked a question related to this

Kargath
  • 450
  • 5
  • 15
  • If you read the error message in the output, the second line tells you what you need to know. You are currently using the value_from function with only one argument, whereas it needs two. – alexheslop1 Oct 11 '22 at 13:42
  • 1
    Also please consider the readability of your code, at the moment it is very hard to decipher what you're trying to do due to the structure and the naming conventions – alexheslop1 Oct 11 '22 at 13:46
  • @alexheslop1 I don't think the error on the first line correctly reveals why the error occurred. Also, you only need to focus on the `call` function, the other parts are just tests to perform function calls simulation – Kargath Oct 11 '22 at 13:52
  • @alexheslop1 Agreed. I fixed the bulk in my answer. Note though that the real magic is just a [Boost Describe example](https://www.boost.org/doc/libs/1_80_0/libs/describe/doc/html/describe.html#example_json_rpc) (Kargath: you can't focus on anything in generic code like this unless you understand how it is used, so yeah "sq = t17[i]" etc. is exceptionally poor naming. And completely unnecessary, compare my answer code :)) – sehe Oct 13 '22 at 02:07

1 Answers1

2

Compiling your code with GCC gives:

<source>:18:18: error: invalid use of void expression
   18 |         (c1.*pmf)(json::value_to<boost::remove_cv_ref_t<A>>(args[I])...));https://godbolt.org/z/E7h4M7bjE

It makes sense because you "reflect" the push member function which returns void. The simplest you can do is to make it return something:

bool push(int val) {
    if (capacity == 0)
        return false;
    if (store.empty() || store.back().size() == capacity)
        store.emplace_back();
    store.back().push(val);
    return true;
}

Now you have more luck with that Boost Describe sample code: https://godbolt.org/z/xbcT3jda1

Fixing The Style

Apart from the non-existent popAt method name, fixing some fallout like:

  • mixing signed/unsigned arithmetic
  • using .at(idx) in favor of .operator[](idx) because I wouldn't vouch for your logic to be correct :)
  • no using namespace
  • moving the #ifdef design into namespace RPC
  • parallel vectors: Just Don't Do It

Live On Coliru

#include <boost/describe.hpp>
#include <boost/json/src.hpp> // for header-only
#include <boost/mp11.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/string_view.hpp>
#include <iostream>
#include <stack>
#include <string>
#include <vector>

namespace json = boost::json;

namespace RPC {
    template <class C1, class C2, class R, class... A, std::size_t... I>
    json::value call_impl_( //
        C1& c1, R (C2::*pmf)(A...), json::array const& args,
        std::index_sequence<I...>) //
    {
        return json::value_from(
            (c1.*pmf)(json::value_to<boost::remove_cv_ref_t<A>>(args[I])...));
    }

    template <class C1, class C2, class R, class... A>
    json::value call_impl(                                   //
        C1& c1, R (C2::*pmf)(A...), 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>
    json::value call(C& c, boost::string_view method,
                     json::value const& args = json::array{}) //
    {
        namespace bd = boost::describe;
        using Fd = bd::describe_members<C, bd::mod_public | bd::mod_function>;

        bool        found = false;
        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;
            }
        });

        return found ? result
                     : throw std::invalid_argument("Invalid method name");
    }
} // namespace RPC

class StackOfPlates {
    using Stack = std::stack<int>;
    std::vector<Stack> store;
    size_t             capacity;

  public:
    StackOfPlates(size_t cap) { capacity = cap; }

    bool push(int val) {
        if (capacity == 0)
            return false;
        if (store.empty() || store.back().size() == capacity)
            store.emplace_back();
        store.back().push(val);
        return true;
    }

    int pop() {
        if (capacity == 0 || store.empty())
            return -1;
        int res = store.back().top();
        store.back().pop();
        if (store.back().empty())
            store.pop_back();
        return res;
    }

    int popAtStack(size_t index) {
        if (capacity == 0 || index >= store.size() || store.at(index).empty())
            return -1;
        int res = store.at(index).top();
        store.at(index).pop();
        if (store.at(index).empty()) {
            store.erase(store.begin() + index);
        }
        return res;
    }
    BOOST_DESCRIBE_CLASS(StackOfPlates, (), (push, pop, popAtStack), (), ())
};

int main() {
    struct {
        std::string method;
        json::array args;
    } actions[]{
        {"push", {1}}, {"push", {2}}, {"popAtStack", {1}},
        {"pop", {}},   {"pop", {}},
    };

    // std::vector init1{1, 1, 2, 2, 2, 3}, init2{1, 4, 5, 2, 5, 4};
    StackOfPlates obj(1); // (init1, init2);

    for (auto& [method, args] : actions)
        std::cout << "RPC Call: " << method << args << " -> "
                  << RPC::call(obj, method, args) << std::endl;
}

Prints

RPC Call: push[1] -> true
RPC Call: push[2] -> true
RPC Call: popAtStack[1] -> 2
RPC Call: pop[] -> 1
RPC Call: pop[] -> -1

See also Compiler explorer GCC, Clang and MSVC

UPDATE

Alternatively you can make the call_impl_ deal with void return types, of course:

if constexpr (std::is_same_v<R, void>) {
    (c1.*pmf)(json::value_to<boost::remove_cv_ref_t<A>>(args[I])...);
    return json::value{};
} else {
    return json::value_from((c1.*pmf)(
        json::value_to<boost::remove_cv_ref_t<A>>(args[I])...));
}

See it live as well: GCC, Clang and MSVC

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Sorry, the code in the `StackOfPlates` part comes from [leetcode](https://leetcode.com/problems/dinner-plate-stacks/), my intention is to implement a code that can be used for [design](https://leetcode.com/ tag/design/)-like function mock calls, allowing me to convert string arguments into corresponding function calls. So the return value of push cannot be modified. Do you have a better way? – Kargath Oct 13 '22 at 02:59
  • No. You just have to fix the problem that void cannot be converted to json. That's not better, just different: e.g [GCC](https://godbolt.org/z/vE8fYfq7z), [Clang](https://godbolt.org/z/Edf9sP58v) and [MSVC](https://godbolt.org/z/bsjd3Mb31) – sehe Oct 14 '22 at 23:51