Bug 1418328

Summary: nbdkit-plugin.h c++ support
Product: [Community] Virtualization Tools Reporter: Shaun McDowell <shaunjmcdowell>
Component: nbdkitAssignee: Richard W.M. Jones <rjones>
Status: CLOSED UPSTREAM QA Contact: Fedora Extras Quality Assurance <extras-qa>
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: unspecifiedCC: eblake, ptoscano, rjones
Target Milestone: ---Keywords: Reopened
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2017-02-01 17:54:13 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
0001-Really-fix-C-support-in-the-nbdkit-plugin.h-header-f.patch none

Description Shaun McDowell 2017-02-01 14:59:25 UTC
Description of problem:
nbdkit-plugin.h uses extern on a number of functions and causes missing symbol errors when compiling in a c++ nbdkit plugin due to c++ name mangling.

Example fix
Use an ifdef to check for __cpluscplus and define LANG "C" if found else set it to blank.

On the extern lines change from
extern void nbdkit_error (const char *msg, ...)

to
extern LANG void nbdkit_error (const char *msg, ...)

Comment 1 Richard W.M. Jones 2017-02-01 15:22:42 UTC
Fixed upstream:
https://github.com/libguestfs/nbdkit/commit/3647fe831a5c6d12832c4e48d1337489fd44fa59

Comment 2 Shaun McDowell 2017-02-01 15:30:47 UTC
The upstream fix doesn't catch this register plugin macro

#define NBDKIT_REGISTER_PLUGIN(plugin)                                  \
  extern struct nbdkit_plugin *                                     \
  plugin_init (void)                                                    \
  {                                                                     \
    (plugin)._struct_size = sizeof (plugin);                            \
    (plugin)._api_version = NBDKIT_API_VERSION;                         \
    (plugin)._thread_model = THREAD_MODEL;                              \
    return &(plugin);                                                   \
  }

leaves the user to call

extern "C" {
  NBDKIT_REGISTER_PLUGIN(myplugin)
}

when they call it for nbdkit to pick it up.

Was also hesitant to extern "C" around the other include files 
 #include <stdarg.h>
 #include <stdint.h>

Comment 3 Richard W.M. Jones 2017-02-01 16:07:15 UTC
Well I can _nearly_ get this to work, but when I try to compile
a plugin with g++, I get:

test-cxx-plugin.cpp:149:1: sorry, unimplemented: non-trivial designated initializers not supported

Apparently you're not allowed to partially declare a struct in C++,
so this isn't legal (or is legal but G++ doesn't support it, I'm not
sure which):

static struct nbdkit_plugin plugin = {                                          
  name              : "cxx",                                                    
  version           : PACKAGE_VERSION,                                          
  load              : cxx_load,                                                 
  open              : cxx_open,                                                 
  get_size          : cxx_get_size,                                             
  pread             : cxx_pread,                                                
};

Comment 4 Richard W.M. Jones 2017-02-01 16:08:16 UTC
Created attachment 1246711 [details]
0001-Really-fix-C-support-in-the-nbdkit-plugin.h-header-f.patch

Interim patch (not working).

Comment 5 Eric Blake 2017-02-01 16:26:56 UTC
(In reply to Richard W.M. Jones from comment #3)

> Apparently you're not allowed to partially declare a struct in C++,
> so this isn't legal (or is legal but G++ doesn't support it, I'm not
> sure which):
> 
> static struct nbdkit_plugin plugin = {                                      
> 
>   name              : "cxx",

It is a language shortcoming of C++.  http://stackoverflow.com/questions/11516657/c-structure-initialization discusses various alternatives, none of them very satisfying for a POD type (which this is). You're pretty much limited to default initialization and per-member assignments, which makes it hard to create a pre-initialized static struct.

Comment 6 Eric Blake 2017-02-01 16:29:14 UTC
Quoting from the link in comment 5, this might be viable:

I found this way of doing it for global variables, that does not require to modify the original structure definition :

struct address {
             int street_no;
             char *street_name;
             char *city;
             char *prov;
             char *postal_code;
           };

then declare the variable of a new type inherited from the original struct type and use the constructor for fields initialisation :

struct temp_address : address { temp_address() { 
    city = "Hamilton"; 
    prov = "Ontario"; 
} } temp_address;

Comment 7 Richard W.M. Jones 2017-02-01 16:32:02 UTC
OK I posted a proper fix (unfortunately rather complex):

https://www.redhat.com/archives/libguestfs/2017-February/msg00004.html

Comment 8 Richard W.M. Jones 2017-02-01 16:33:12 UTC
(In reply to Eric Blake from comment #6)
> Quoting from the link in comment 5, this might be viable:
> 
> I found this way of doing it for global variables, that does not require to
> modify the original structure definition :
> 
> struct address {
>              int street_no;
>              char *street_name;
>              char *city;
>              char *prov;
>              char *postal_code;
>            };
> 
> then declare the variable of a new type inherited from the original struct
> type and use the constructor for fields initialisation :
> 
> struct temp_address : address { temp_address() { 
>     city = "Hamilton"; 
>     prov = "Ontario"; 
> } } temp_address;

Hmm OK, I'll see if that works ...

The way I did it in the patch above was the use __attribute__((constructor))
to include a function that would run before plugin_init was called.

Comment 9 Shaun McDowell 2017-02-01 16:35:38 UTC
you can also use a static initializer function

example of anon namespace initializer

namespace {
nbdkit_plugin create_my_plugin() {
	nbdkit_plugin plugin = nbdkit_plugin();
	plugin.name = plugin_name;
	plugin.load = plugin_load;
	plugin.unload = plugin_unload;
	plugin.config = plugin_config;
	plugin.config_complete = plugin_config_complete;
	plugin.config_help = plugin_config_help;
	plugin.open = plugin_open;
	plugin.close = plugin_close;
	plugin.get_size = plugin_get_size;
	plugin.pread = plugin_pread;
	plugin.pwrite = plugin_pwrite;
	plugin.flush = plugin_flush;
	plugin.trim = plugin_trim;

	return plugin;
}
}

static struct nbdkit_plugin = create_my_plugin();

example of c++11 lambda initializer 
static nbdkit_plugin plugin = []() -> nbdkit_plugin {
	nbdkit_plugin plugin = nbdkit_plugin();
	plugin.name = plugin_name;
	plugin.load = plugin_load;
	plugin.unload = plugin_unload;
	plugin.config = plugin_config;
	plugin.config_complete = plugin_config_complete;
	plugin.config_help = plugin_config_help;
	plugin.open = plugin_open;
	plugin.close = plugin_close;
	plugin.get_size = plugin_get_size;
	plugin.pread = plugin_pread;
	plugin.pwrite = plugin_pwrite;
	plugin.flush = plugin_flush;
	plugin.trim = plugin_trim;

	return plugin;
}();

Comment 10 Richard W.M. Jones 2017-02-01 16:38:29 UTC
Patch v2 posted:
https://www.redhat.com/archives/libguestfs/2017-February/msg00007.html

Comment 11 Richard W.M. Jones 2017-02-01 16:41:54 UTC
Yeah I guess comment 9 is better.

Patch v3 posted:

https://www.redhat.com/archives/libguestfs/2017-February/msg00009.html

Comment 12 Richard W.M. Jones 2017-02-01 17:54:13 UTC
OK I pushed the v3 patch upstream.