[libvirt] [GSoC] Code design for scalar and external types
Daniel P. Berrangé
berrange at redhat.com
Mon Jun 18 10:52:04 UTC 2018
On Mon, Jun 18, 2018 at 12:24:39PM +0200, Pavel Hrdina wrote:
> Hi,
>
> It took me longer to sit down and write this mail but here it goes.
>
> There was a lot of ideas about the macro design and it's easy to
> get lost in all the designs.
>
> So we agreed on this form:
>
>
> # define VIR_AUTOPTR_FUNC_NAME(type) virAutoPtr##type
> # define VIR_AUTOCLEAR_FUNC_NAME(type) virAutoClear##type
>
> # define VIR_DEFINE_AUTOPTR_FUNC(type, func) \
> static inline void VIR_AUTOPTR_FUNC_NAME(type)(type *_ptr) \
> { \
> (func)(*_ptr); \
> }
>
> # define VIR_DEFINE_AUTOCLEAR_FUNC(type, func) \
> static inline void VIR_AUTOCLEAR_FUNC_NAME(type)(type *_ptr) \
> { \
> (func)(_ptr); \
> }
For anything where we define the impl of (func) these two macros
should never be used IMHO. We should just change the signature of
the real functions so that accept a double pointer straight away,
and thus not require the wrapper function. Yes, it this will
require updating existing code, but such a design change is
beneficial even without the cleanup macros, as it prevents the
possbility of double frees. I'd suggest we just do this kind of
change straightaway
> # define VIR_AUTOFREE(type) __attribute__((cleanup(virFree))) type
I don't see the point in passing "type" in here we could simplify
this:
#define VIR_AUTOFREE __attribute__((cleanup(virFree))
VIR_AUTOFREE char *foo = NULL;
and at the same time fix the char ** problems
#define VIR_AUTOFREE_FUNC(func) __attribute__((cleanup(func))
VIR_AUTOFREE_FUNC(virStringListFree) char *foo = NULL;
or if we want to simplify it
#define VIR_AUTOFREE_STRINGLIST VIR_AUTOFREE_FUNC(virStringListFree)
VIR_AUTOFREE_STRINGLIST char **strs = NULL;
This also works for external libraries
> # define VIR_AUTOPTR(type) \
> __attribute__((cleanup(VIR_AUTOPTR_FUNC_NAME(type)))) type
>
> # define VIR_AUTOCLEAR(type) \
> __attribute__((cleanup(VIR_AUTOCLEAR_FUNC_NAME(type)))) type
>
>
> but it turned out the it's not sufficient for several reasons:
>
> - there is no simple way ho to free list of strings (char **)
> - this will not work for external types with their own free function
>
>
> In order to solve these two issues there were some ideas.
>
>
> 1. How to handle list of strings
>
> We have virStringListFree() function in order to free list of strings,
> the question is how to use it with these macros:
>
> - Create a new typedef for list of strings and use VIR_AUTOPTR:
>
> typedef char **virStringList;
>
> VIR_DEFINE_AUTOPTR_FUNC(virStringList, virStringListFree);
>
> VIR_AUTOPTR(virStringList) list = NULL;
I don't think we should be creating artifical new typedefs just to
deal with the flawed design of our own autofree macros.
> - Overload VIR_AUTOFREE macro by making it variadic
>
> # define _VIR_AUTOFREE_0(type) __attribute__((cleanup(virFree))) type
> # define _VIR_AUTOFREE_1(type, func) __attribute__((cleanup(func))) type
>
> # define _VIR_AUTOFREE_OVERLOADER(_1, _2, NAME, ...) NAME
> # define VIR_AUTOFREE(...) _VIR_AUTOFREE_OVERLOADER(__VA_ARGS__, _VIR_AUTOFREE_1, _VIR_AUTOFREE_0)(__VA_ARGS__)
This is uneccessarily black magic - just have VIR_AUTOFREE and
VIR_AUTOFREE_FUNC defined separately.
> void virStringListPtrFree(char ***stringsp)
> {
> virStringListFree(*stringsp);
> }
As above, we should just fixed virStringListFree to have the better
signature straight away.
> VIR_AUTOFREE(char **, virStringListPtrFree) list = NULL;
>
>
> 2. External types with free function
>
> Libvirt uses a lot of external libraries where we use the types and
> relevant free functions. The issue with original design is that it was
> counting on the fact that we would have vir*Ptr typedefs, but that's not
> the case for external types.
>
> - We can modify the design to be closer to GLib design which would
> work even with external types and would be safe in case external
> free functions will not behave correctly. These are to
> modification to the original design:
>
> # define VIR_AUTOPTR_TYPE_NAME(type) ##typeAutoPtr
>
> # define VIR_DEFINE_AUTOPTR_FUNC(type, func) \
> typedef type *VIR_AUTOPTR_TYPE_NAME(type);
> static inline void VIR_AUTOPTR_FUNC_NAME(type)(type **_ptr) \
> { \
> if (*_ptr) { \
> (func)(*_ptr); \
> *_ptr = NULL; \
> } \
> }
>
> # define VIR_AUTOPTR(type) \
> __attribute__((cleanup(VIR_AUTOPTR_FUNC_NAME(type)))) VIR_AUTOPTR_TYPE_NAME(type)
>
> The usage would not require our internal vir*Ptr types and would
> be easy to use with external types as well:
>
> VIR_DEFINE_AUTOPTR_FUNC(virBitmap, virBitmapFree);
>
> VIR_AUTOPTR(virBitmap) map = NULL;
>
> VIR_DEFINE_AUTOPTR_FUNC(LIBSSH2_CHANNEL, libssh2_channel_free);
Contrary to my general point above about VIR_DEFINE_AUTOPTR_FUNC,
this is a reasonable usage, because we don't control the signature
of the libssh2_channel_free function.
> VIR_AUTOPTR(LIBSSH2_CHANNEL) channel = NULL;
With my example above
#define VIR_AUTOFREE_LIBSSH_CHANNEL VIR_AUTOFREE_FUNC(LIBSSH2_CHANNEL)
VIR_AUTOFREE_LIBSSH_CHANNEL LIBSSH_CHANNEL *chan = NULL;
Regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
More information about the libvir-list
mailing list