dbus-cplusplus/tools/generate_adaptor.cpp
James Harris 63c7175250 Remove warnings from generated adaptors.
This patch modifies the dbusxx-xml2cpp's generate_adaptor function  so
that it prevents generation of the MessageIter ri in cases where an
interface method has no input arguments.

By making this element conditional based on the presence of input
arguments we avoid "unused-but-set-variable" warning in code that uses
the DBus-C++ adaptor glue headers.

Change-Id: I0f04439bf7b2b3cecb9d639e61c2b6dedc148e51
Type: Code Improvement
2014-03-19 12:52:16 +01:00

725 lines
22 KiB
C++

/*
*
* D-Bus++ - C++ bindings for D-Bus
*
* Copyright (C) 2005-2007 Paolo Durante <shackan@gmail.com>
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <algorithm>
#include "generator_utils.h"
#include "generate_adaptor.h"
using namespace std;
using namespace DBus;
extern const char *tab;
extern const char *header;
extern const char *dbus_includes;
/*! Generate adaptor code for a XML introspection
*/
void generate_adaptor(Xml::Document &doc, const char *filename)
{
ostringstream body;
ostringstream head;
vector <string> include_vector;
head << header;
string filestring = filename;
underscorize(filestring);
string cond_comp = "__dbusxx__" + filestring + "__ADAPTOR_MARSHAL_H";
head << "#ifndef " << cond_comp << endl
<< "#define " << cond_comp << endl;
head << dbus_includes;
Xml::Node &root = *(doc.root);
Xml::Nodes interfaces = root["interface"];
// iterate over all interface definitions
for (Xml::Nodes::iterator i = interfaces.begin(); i != interfaces.end(); ++i)
{
Xml::Node &iface = **i;
Xml::Nodes methods = iface["method"];
Xml::Nodes signals = iface["signal"];
Xml::Nodes properties = iface["property"];
Xml::Nodes ms;
ms.insert(ms.end(), methods.begin(), methods.end());
ms.insert(ms.end(), signals.begin(), signals.end());
// gets the name of a interface: <interface name="XYZ">
string ifacename = iface.get("name");
// these interface names are skipped.
if (ifacename == "org.freedesktop.DBus.Introspectable"
|| ifacename == "org.freedesktop.DBus.Properties")
{
cerr << "skipping interface " << ifacename << endl;
continue;
}
istringstream ss(ifacename);
string nspace;
unsigned int nspaces = 0;
// prints all the namespaces defined with <interface name="X.Y.Z">
while (ss.str().find('.', ss.tellg()) != string::npos)
{
getline(ss, nspace, '.');
body << "namespace " << nspace << " {" << endl;
++nspaces;
}
body << endl;
string ifaceclass;
getline(ss, ifaceclass);
// a "_adaptor" is added to class name to distinguish between proxy and adaptor
ifaceclass += "_adaptor";
cerr << "generating code for interface " << ifacename << "..." << endl;
// the code from class definiton up to opening of the constructor is generated...
body << "class " << ifaceclass << endl
<< ": public ::DBus::InterfaceAdaptor" << endl
<< "{" << endl
<< "public:" << endl
<< endl
<< tab << ifaceclass << "()" << endl
<< tab << ": ::DBus::InterfaceAdaptor(\"" << ifacename << "\")" << endl
<< tab << "{" << endl;
// generates code to bind the properties
for (Xml::Nodes::iterator pi = properties.begin(); pi != properties.end(); ++pi)
{
Xml::Node &property = **pi;
body << tab << tab << "bind_property("
<< property.get("name") << ", "
<< "\"" << property.get("type") << "\", "
<< (property.get("access").find("read") != string::npos
? "true"
: "false")
<< ", "
<< (property.get("access").find("write") != string::npos
? "true"
: "false")
<< ");" << endl;
}
// generate code to register all methods
for (Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi)
{
Xml::Node &method = **mi;
body << tab << tab << "register_method("
<< ifaceclass << ", " << method.get("name") << ", " << stub_name(method.get("name"))
<< ");" << endl;
}
body << tab << "}" << endl
<< endl;
body << tab << "::DBus::IntrospectedInterface *introspect() const " << endl
<< tab << "{" << endl;
// generate the introspect arguments
for (Xml::Nodes::iterator mi = ms.begin(); mi != ms.end(); ++mi)
{
Xml::Node &method = **mi;
Xml::Nodes args = method["arg"];
body << tab << tab << "static ::DBus::IntrospectedArgument " << method.get("name") << "_args[] = " << endl
<< tab << tab << "{" << endl;
for (Xml::Nodes::iterator ai = args.begin(); ai != args.end(); ++ai)
{
Xml::Node &arg = **ai;
body << tab << tab << tab << "{ ";
if (arg.get("name").length())
{
body << "\"" << arg.get("name") << "\", ";
}
else
{
body << "0, ";
}
body << "\"" << arg.get("type") << "\", "
<< (arg.get("direction") == "in" ? "true" : "false")
<< " }," << endl;
}
body << tab << tab << tab << "{ 0, 0, 0 }" << endl
<< tab << tab << "};" << endl;
}
body << tab << tab << "static ::DBus::IntrospectedMethod " << ifaceclass << "_methods[] = " << endl
<< tab << tab << "{" << endl;
// generate the introspect methods
for (Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi)
{
Xml::Node &method = **mi;
body << tab << tab << tab << "{ \"" << method.get("name") << "\", " << method.get("name") << "_args }," << endl;
}
body << tab << tab << tab << "{ 0, 0 }" << endl
<< tab << tab << "};" << endl;
body << tab << tab << "static ::DBus::IntrospectedMethod " << ifaceclass << "_signals[] = " << endl
<< tab << tab << "{" << endl;
for (Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si)
{
Xml::Node &method = **si;
body << tab << tab << tab << "{ \"" << method.get("name") << "\", " << method.get("name") << "_args }," << endl;
}
body << tab << tab << tab << "{ 0, 0 }" << endl
<< tab << tab << "};" << endl;
body << tab << tab << "static ::DBus::IntrospectedProperty " << ifaceclass << "_properties[] = " << endl
<< tab << tab << "{" << endl;
for (Xml::Nodes::iterator pi = properties.begin(); pi != properties.end(); ++pi)
{
Xml::Node &property = **pi;
body << tab << tab << tab << "{ "
<< "\"" << property.get("name") << "\", "
<< "\"" << property.get("type") << "\", "
<< (property.get("access").find("read") != string::npos
? "true"
: "false")
<< ", "
<< (property.get("access").find("write") != string::npos
? "true"
: "false")
<< " }," << endl;
}
body << tab << tab << tab << "{ 0, 0, 0, 0 }" << endl
<< tab << tab << "};" << endl;
// generate the Introspected interface
body << tab << tab << "static ::DBus::IntrospectedInterface " << ifaceclass << "_interface = " << endl
<< tab << tab << "{" << endl
<< tab << tab << tab << "\"" << ifacename << "\"," << endl
<< tab << tab << tab << ifaceclass << "_methods," << endl
<< tab << tab << tab << ifaceclass << "_signals," << endl
<< tab << tab << tab << ifaceclass << "_properties" << endl
<< tab << tab << "};" << endl
<< tab << tab << "return &" << ifaceclass << "_interface;" << endl
<< tab << "}" << endl
<< endl;
body << "public:" << endl
<< endl
<< tab << "/* properties exposed by this interface, use" << endl
<< tab << " * property() and property(value) to get and set a particular property" << endl
<< tab << " */" << endl;
// generate the properties code
for (Xml::Nodes::iterator pi = properties.begin(); pi != properties.end(); ++pi)
{
Xml::Node &property = **pi;
string name = property.get("name");
string type = property.get("type");
string type_name = signature_to_type(type);
body << tab << "::DBus::PropertyAdaptor< " << type_name << " > " << name << ";" << endl;
}
body << endl;
body << "public:" << endl
<< endl
<< tab << "/* methods exported by this interface," << endl
<< tab << " * you will have to implement them in your ObjectAdaptor" << endl
<< tab << " */" << endl;
// generate the methods code
for (Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi)
{
Xml::Node &method = **mi;
Xml::Nodes args = method["arg"];
Xml::Nodes args_in = args.select("direction", "in");
Xml::Nodes args_out = args.select("direction", "out");
Xml::Nodes annotations = args["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
body << tab << "virtual ";
// return type is 'void' if none or multible return values
if (args_out.size() == 0 || args_out.size() > 1)
{
body << "void ";
}
else if (args_out.size() == 1)
{
// generate basic or object return type
if (arg_object.length())
{
body << arg_object << " ";
}
else
{
body << signature_to_type(args_out.front()->get("type")) << " ";
}
}
// generate the method name
body << method.get("name") << "(";
// generate the methods 'in' variables
unsigned int i = 0;
for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
{
Xml::Node &arg = **ai;
Xml::Nodes annotations = arg["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_name = arg.get("name");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
// generate basic signature only if no object name available...
if (!arg_object.length())
{
body << "const " << signature_to_type(arg.get("type")) << "& ";
}
// ...or generate object style if available
else
{
body << "const " << arg_object << "& ";
// store a object name to later generate header includes
include_vector.push_back(arg_object);
}
if (arg_name.length())
body << arg_name;
if ((i + 1 != args_in.size() || args_out.size() > 1))
body << ", ";
}
// generate the method 'out' variables if multibe 'out' values exist
if (args_out.size() > 1)
{
unsigned int i = 0;
for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
{
Xml::Node &arg = **ao;
Xml::Nodes annotations = arg["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_name = arg.get("name");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
// generate basic signature only if no object name available...
if (!arg_object.length())
{
body << signature_to_type(arg.get("type")) << "& ";
}
// ...or generate object style if available
else
{
body << arg_object << "& ";
// store a object name to later generate header includes
include_vector.push_back(arg_object);
}
if (arg_name.length())
body << arg_name;
if (i + 1 != args_out.size())
body << ", ";
}
}
body << ") = 0;" << endl;
}
body << endl
<< "public:" << endl
<< endl
<< tab << "/* signal emitters for this interface" << endl
<< tab << " */" << endl;
// generate the signals code
for (Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si)
{
Xml::Node &signal = **si;
Xml::Nodes args = signal["arg"];
body << tab << "void " << signal.get("name") << "(";
// generate the signal arguments
unsigned int i = 0;
for (Xml::Nodes::iterator a = args.begin(); a != args.end(); ++a, ++i)
{
Xml::Node &arg = **a;
Xml::Nodes annotations = arg["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
// generate basic signature only if no object name available...
if (!arg_object.length())
{
body << "const " << signature_to_type(arg.get("type")) << "& arg" << i + 1;
}
// ...or generate object style if available
else
{
body << "const " << arg_object << "& arg" << i + 1;
// store a object name to later generate header includes
include_vector.push_back(arg_object);
}
if (i + 1 != args.size())
body << ", ";
}
body << ")" << endl
<< tab << "{" << endl
<< tab << tab << "::DBus::SignalMessage sig(\"" << signal.get("name") << "\");" << endl;
// generate the signal body
if (!args.empty())
{
body << tab << tab << "::DBus::MessageIter wi = sig.writer();" << endl;
unsigned int i = 0;
for (Xml::Nodes::iterator a = args.begin(); a != args.end(); ++a, ++i)
{
Xml::Node &arg = **a;
Xml::Nodes annotations = arg["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
if (arg_object.length())
{
body << tab << tab << signature_to_type(arg.get("type")) << " _arg" << i + 1 << ";" << endl;
body << tab << tab << "_arg" << i + 1 << " << " << "arg" << i + 1 << ";" << endl;
body << tab << tab << "wi << _arg" << i + 1 << ";" << endl;
}
else
{
body << tab << tab << "wi << arg" << i + 1 << ";" << endl;
}
}
}
// emit the signal in method body
body << tab << tab << "emit_signal(sig);" << endl
<< tab << "}" << endl;
}
body << endl
<< "private:" << endl
<< endl
<< tab << "/* unmarshalers (to unpack the DBus message before calling the actual interface method)" << endl
<< tab << " */" << endl;
// generate the unmarshalers
for (Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi)
{
Xml::Node &method = **mi;
Xml::Nodes args = method["arg"];
Xml::Nodes args_in = args.select("direction", "in");
Xml::Nodes args_out = args.select("direction", "out");
body << tab << "::DBus::Message " << stub_name(method.get("name")) << "(const ::DBus::CallMessage &call)" << endl
<< tab << "{" << endl;
if(!args_in.empty())
{
body << tab << tab << "::DBus::MessageIter ri = call.reader();" << endl;
body << endl;
}
// generate the 'in' variables
unsigned int i = 1;
for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
{
Xml::Node &arg = **ai;
body << tab << tab << signature_to_type(arg.get("type")) << " argin" << i << ";" << " ";
body << "ri >> argin" << i << ";" << endl;
}
// generate the 'in' object variables
i = 1;
for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
{
Xml::Node &arg = **ai;
Xml::Nodes annotations = arg["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
if (arg_object.length())
{
body << tab << tab << arg_object << " _argin" << i << ";";
body << " " << "_argin" << i << " << " << "argin" << i << ";" << endl;
}
}
// generate 'out' variables
if (!args_out.empty())
{
unsigned int i = 1;
for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
{
Xml::Node &arg = **ao;
body << tab << tab << signature_to_type(arg.get("type")) << " argout" << i;
if (args_out.size() == 1) // a single 'out' parameter will be assigned
{
body << " = ";
}
else // multible 'out' parameters will be handled as parameters below
{
body << ";" << endl;
}
}
}
// generate 'out' object variables
if (!args_out.empty())
{
unsigned int i = 1;
for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
{
Xml::Node &arg = **ao;
Xml::Nodes annotations = arg["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
// generate object types
if (arg_object.length())
{
body << tab << tab << arg_object << " _argout" << i << ";" << endl;
}
}
}
// generate in '<<' operation
i = 0;
for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
{
Xml::Node &arg = **ai;
Xml::Nodes annotations = arg["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
}
// do correct indent
if (args_out.size() != 1)
{
body << tab << tab;
}
body << method.get("name") << "(";
// generate call stub parameters
i = 0;
for (Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i)
{
Xml::Node &arg = **ai;
Xml::Nodes annotations = arg["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
if (arg_object.length())
{
body << "_argin" << i + 1;
}
else
{
body << "argin" << i + 1;
}
if ((i + 1 != args_in.size() || args_out.size() > 1))
body << ", ";
}
if (args_out.size() > 1)
{
i = 0;
for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
{
Xml::Node &arg = **ao;
Xml::Nodes annotations = arg["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
if (arg_object.length())
{
body << "_argout" << i + 1;
}
else
{
body << "argout" << i + 1;
}
if (i + 1 != args_out.size())
body << ", ";
}
}
body << ");" << endl;
body << tab << tab << "::DBus::ReturnMessage reply(call);" << endl;
if (!args_out.empty())
{
body << tab << tab << "::DBus::MessageIter wi = reply.writer();" << endl;
// generate out '<<' operation
i = 0;
for (Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i)
{
Xml::Node &arg = **ao;
Xml::Nodes annotations = arg["annotation"];
Xml::Nodes annotations_object = annotations.select("name", "org.freedesktop.DBus.Object");
string arg_object;
if (!annotations_object.empty())
{
arg_object = annotations_object.front()->get("value");
}
if (arg_object.length())
{
body << tab << tab << "argout" << i + 1 << " << " << "_argout" << i + 1 << ";" << endl;
}
}
for (unsigned int i = 0; i < args_out.size(); ++i)
{
body << tab << tab << "wi << argout" << i + 1 << ";" << endl;
}
}
body << tab << tab << "return reply;" << endl;
body << tab << "}" << endl;
}
body << "};" << endl
<< endl;
for (unsigned int i = 0; i < nspaces; ++i)
{
body << "} ";
}
body << endl;
}
body << "#endif //" << cond_comp << endl;
// remove all duplicates in the header include vector
vector<string>::const_iterator vec_end_it = unique(include_vector.begin(), include_vector.end());
for (vector<string>::const_iterator vec_it = include_vector.begin();
vec_it != vec_end_it;
++vec_it)
{
const string &include = *vec_it;
head << "#include " << "\"" << include << ".h" << "\"" << endl;
}
head << endl;
ofstream file(filename);
if (file.bad())
{
cerr << "unable to write file " << filename << endl;
exit(-1);
}
file << head.str();
file << body.str();
file.close();
}