-2

I have a legacy C code base, which I am migrating to C++ in a piecemeal fashion. It includes an interpreter, so there is a need to wrap static functions and arguments for use by the interpreter. So a typical function for export to the interpreter may have the following signature:

static void do_strstr(struct value * p)

and be exposed to the interpreter like so:

using vptr = void (*) ();
template <typename Func>
constexpr vptr to_vptr(Func && func)
{ return reinterpret_cast<vptr>(func); }


struct function string_funs[] = {
     ...
        { C_FN3,        X_A3,   "SSI",  to_vptr(do_strstr),     "find" },
    ...
    };

This has been proven to work. The drawback with the method so far is that the called function must allocate memory onto a temporary stack. An improvement would be where the called function just returns a string, for example. This function is then wrapped, where the wrapper does the memory magic behind the scenes. This allows functions to created in a more vanilla way.

Here is an implementation which concatenates two strings using my improved method:

static std::string do_concata(struct value* p)
{
        std::string s1 = (p)->gString();
        std::string s2 = (p+1)->gString();
        return s1+s2;
}

I create a helper function:

static void do_concata_1(struct value* p)
{
        wrapfunc(do_concata)(p);
}

where the somewhat generic wrapper is defined as:

std::function<void(struct value*)>
wrapfunc(std::function<std::string(struct value*)> func)
{
        auto fn = [=](struct value* p) { 
                std::string s = func(p);
                char* ret = alloc_tmp_mem(s.size()+1);          
                strcpy(ret,  s.c_str());
                p->sString(ret);
                return; 
        };
        return fn;
}

which is exposed to the interpreter as follows:

struct function string_funs[] = {
...
{ C_FN2,        X_A2,   "SS",    to_vptr(do_concata_1),    "concata" },
...

};

I am not satisfied with this solution, though, as it requires a helper function for each function I define. It would be better if I could eliminate do_concata_1 and write another function that wraps the wrapfunc.

And this is where the problem is. If I write:

vptr to_vptr_1(std::function<void(struct value*)> func)
{
        return to_vptr(wrapfunc(func));
}

then the compiler complains:

stringo.cc: In function ‘void (* to_vptr_1(std::function<void(value*)>))()’:
stringo.cc:373:30: error: could not convert ‘func’ from ‘std::function<void(value*)>’ to ‘std::function<std::__cxx11::basic_string<char>(value*)>’
  return to_vptr(wrapfunc(func));

which is bizarre in my mind, because where did the std::__cxx11::basic_string<char> come from? It should be void, surely?

I'm at a loss to see what the fix should be. I am also a bit confused as to whether I should be passing copies of functions, references to functions, or the enigmatic && r-vale references.

blippy
  • 1,490
  • 1
  • 13
  • 22
  • The only realistic fix here is to burn the whole thing with fire, and reimplement it, from scratch, in type-safe form using modern C++. – Sam Varshavchik Apr 21 '18 at 14:04
  • @Sam. The codebase is hairy, that's true, but I'd rather refactor than rewrite. Remember, too, that because the functions are being used in an interpreter, there has to be a mechanism for wrapping and unwrapping functions anyway. The whole alloc_tmp_mem() and strcpy() is unfortunate, but not a disaster. At least with my proposed refactoring a pinch-point is created in the code. This will allow me to improve the code at a single point later date. – blippy Apr 21 '18 at 14:17

1 Answers1

0

In to_vptr_1(), func is established as a function that returns void. But func is passed to wrapfunc(), which expects a function that returns std::string. The compiler does not have a way to convert func from std::function<void(struct value*)> to std::function<std::string(struct value*)>, so it emits the error message.

reinterpret_cast from std::function to raw function pointer is not going to work. This question has some good discussion on the topic, and this one has a solution that could perhaps be reworked for this situation.

Jack C.
  • 744
  • 3
  • 7