Fwd: C++ libubus wrapper

Felix Fietkau nbd at nbd.name
Thu Feb 4 13:29:51 EST 2021


On 2021-02-04 18:25, Wojciech Jowsa wrote:
> czw., 4 lut 2021 o 10:32 Felix Fietkau <nbd at nbd.name> napisał(a):
>>
>>
>> On 2021-02-03 20:02, Wojciech Jowsa wrote:
>> > Hi,
>> >
>> > I would like to write a libubus wrapper in C++. When looking into
>> > ubus_method and ubus_handler_t structures I don't see a place where I
>> > could pass a this pointer. It makes the handling of ubus calls in C++
>> > a bit complicated.
>> > The solution to that case would be adding a void pointer to the
>> > ubus_method structure (like in the ubus_request struct). This way it
>> > would be possible to pass e.g function pointer.
>> > Would it be possible to add a void pointer to ubus_method structure?
>> > If yes then I will provide the patch asap.
>> I don't think a 'this' pointer should be in the ubus_method. Methods can
>> typically be shared between multiple ubus_objects, and the 'this'
>> pointer would typically go into the ubus_object.
> 
> The pointer would share the same concept as the .handler member of
> ubus_method structure.
> It means that C++ handler method would be called based on the ubus method name.
> Void pointer could also be the last argument of ubus_handler_t. Then
> I see that e.g. ubus_request structure has a void pointer as a member
> or ubus_lookup has a void pointer
> parameter so the idea is implemented but only partially.
The idea has been implemented selectively where it makes sense.
In the ubus request it is needed, because functions like ubus_invoke()
create it on-stack, and there is no embedding into other data structures.

>> However, even there it is not necessary to have a void pointer directly
>> in the struct. You can simply embed the ubus_object in another data
>> structure which contains the 'this' pointer (or maybe is even embedded
>> in the C++ object directly)
> 
> Then obtain parent struct with container_of? This might somehow work
> but personally I have never tried that.
> I think that the common pattern for C libraries API methods is to
> allow passing a user data through a void pointer either as
> a function parameter or as a structure member.
For me, putting void * pointers everywhere is an anti-pattern which I
discourage with the way the ubus api is set up. It is a lazy way to
avoid making the code more type safe, and it adds a bit of unnecessary
bloat to data structures.

The ubus API is designed in a way that you typically embed the struct
ubus_object into your own object-like data structure.
Simple pseudo-code example:

struct my_obj {
	...
	struct ubus_object ubus;
	...
};


static int
my_obj_test(struct ubus_context *ctx, struct ubus_object *obj,
            struct ubus_request_data *req, const char *method,
            struct blob_attr *msg)
{
	struct my_obj *obj = container_of(obj, struct my_obj, ubus);

	...
}

static struct ubus_method my_obj_methods[] = {
	{ .name = "test", .handle = my_obj_test },
};

As you can see, this pattern preserves some type-safety when going from
struct ubus_object to struct my_obj and it does not waste space for an
extra pointer. It is also more efficient, since the compiler will turn
it into simple pointer arithmetic instead of an extra memory access.

I think it would be very useful if your C++ wrapper could preserve this
pattern and make it just as convenient to use.

Maybe a C++ wrapper around ubus_object could simply be embedded as a
class attribute in user-defined classes and you could write some code to
generate thin wrappers that derive the object pointer from the address
of the struct ubus_object pointer and call plain C++ methods with a
fixed type.

I don't know enough C++ to come up with an example of what that wrapper
would look like, but it seems to me that if C can do this, it should be
possible in C++ as well.

Does that make sense?

- Felix



More information about the openwrt-devel mailing list