Wrap C++ function using Boost Reflect or another C

2019-07-17 05:55发布

问题:

I posted this question. SpongeBobFan said the answer is reflection. I found Boost.Reflect and was wondering how the heck I can pull this off using that library or another C++ reflection library. Please explain your answer, as I cannot just glance at code and figure out what's going on. My question was this:

Ok, I have a question. Say I have this code:

int myfunc(int arg-a, int arg-b);
int mywrapperfunc(obj a, obj b);

mywrapperfunc is supposed to wrap myfunc. mywrapperfunc discards the first argument and takes the second, which is an array. I then uses the array items as parameters. But say I don't know how many parameters myfunc takes, nor do I know how many items are in the array-type object (b). How would I programatically call myfunc with the correct number of args? The number of args handed over would be the same as the number of items in the array-type object.

EDIT: arg-a and arg-b are supposed to come from the array-type object. I split the object into the args.

EDIT: Ok, ok, I'm trying to wrap the Python C API with some sense involved, hiding most background jobs.

回答1:

Have you looked at Reflex? This tool uses GCC-XML to create a reflection library that is similar to the one in Java.

Here's my initial take on it (I currently don't have a lot of free time to flush it out further):

// Use the Reflex library to look up "myfunc" reflectively
Type t = Type::ByName("myfunc");
// The Reflex type 'Type" has a "FunctionParameterSize()" that tells
// you how many args in a function
size_t num_params = t.FunctionParameterSize();
// Use this information to call "myfunc" with objects from "b"

If Reflex only provides the invocation capability on a member of a class, then you could make myfunc() a static class member, and try something like this:

// Load MyClass reflectively (where MyClass contains a static member function "myfunc")
Type t = Type::ByName("MyClass");
// Find the member function "myfunc"
Member m = t.MemberByName("myfunc");
// Pack the parameters from "b" into a vector
// (Assumes "b" has indexing operators, and a "length()" function)
std::vector params(&b[0], &b[b.length()-1]);
// Invoke the method "myfunc" reflectively
m.Invoke(params);

You'll have to fiddle with this, I'm not sure if I'm doing the parameter passing correctly, etc. but hopefully this gives you some new ideas.



回答2:

The GCCXML/Reflex scheme suggested in a another answer essentially proposes that you run a step to preprocess your code, and extract information about the set of classes/functions/fields you have, so that you can programmatically access them. And given your stated that, that sounds like the right direction.

The Reflex scheme appears (I don't know anything about except what is posted in the other answer) to make this extra infromation available at runtime via a library that looks like dynamic reflection provided by other reflectable-languages, at the price of bloating the application with all this reflection data that you mostly don't use (the excuse for C++ was, "don't pay for what you don't use"). The point is you still have to write these psuedo-reflection calls to achieve your purpose, so you must know in advance to a great degree what you actually want to do.

You don't have to do it that way. If you have to preprocess the source code, you can get a preprocessor to extract the descriptive information (sort of like GCCXML), and simply generate calls on the targeted functions. The resulting program will do what the "reflection" one would do, but there's no runtime reflection library or bloat.

You can do this in theory with any program transformation. IN practice, such a tool must be able to process C++ and that's extremely hard.

There's only three tools on earth that can do this: our DMS Software Reengineering Toolkit with its C++ front end, Clang and (distant third) GCC.

All provide parsing of code, building ASTs and symbol tables (this is the reflection data you want), including all the microscopic details such as "number of arguments" (which appears to be a shortcoming of the Reflect scheme based on a comment in the other answer).

GCC can't regenerate valid C++ code although there is some extension called GCCMelt which makes some claim about this, that I have no experience with. (GCCXML is a custom hack of GCC to dump the symbol table data). Clang does provide the ability to modify C++ ASTs, but you have to hack at the trees through a procedural interface, which makes writing the transformations hard. DMS provides source-to-source surface syntax transformation capability, making it easier to write transformations. (YMMV).

The point is that by using such tools, you can write custom code to walk the symbol table to extract the function definitions etc. you want, and generate C++ code that does what you want (variable length argument lists and all). [This may be more complicated than you expect, but that's not different than the online reflection solution you already know about). No runtime reflection needed.

Don't expect any of these solutions to be easy to implement. C++ is a complicated language, and you'll pay the price for its complexity in whatever implementation scheme you choose, unless you are doing something really simple.



回答3:

Unfortunately, I don't think exactly what you want is feasible in C++. Since C++ code goes through 2 sequential stages, 1) Compilation and 2) Execution, you generally can't take something that is not known until execution time (the number of arguments needed by myfunc) and hand it back to the compiler so that the compiler can find the right myfunc to compile against.

Now, if myfunc() (which I assume is some Python C API call) is declared something like C's printf statement, you have some options. Printf in C is declared with a variable argument list, for example, stdio.h probably has a declaration similar to:

int printf ( const char * format, ... );

This allows printf to take any number of arguments. But from your description, that does not sound like the function (or set of functions?) that you want to wrap. Sometimes C++ template meta-programming techniques offer something close to what you want: if you can calculate the number of parameters at compile time, you can use a template class to choose the right prototype of myfunc to invoke. For example, maybe something like:

template<int N>
class Wrapper {
  public:
    mywrapperfunc() {
      if (N == 0) {
        // Call myfunc() with zero args here...
      } else if (N == 1) {
        // Call myfunc() with 1 arg here...
      } // etc...
    }
};

C++11 allows you some leeway in calculating the value of "N" to use at compile time via the constexpr keyword. But again, you must be able to know, or at least calculate, N at compile time for this to work.

Honestly, I can't think of any other way to make this work. Through many years of programming, I have come up with two basic rules that define this type of situation:

  1. If you have come up with a cool idea for a library, someone else has probably already written it, and it's probably better than your idea. Look harder.
  2. If nobody else has come up with that library yet, and you can't figure out (after a lot of research) how to do it, it probably can't be done with the technology that you're using (e.g.: C++).