Вопрос

I'm currently working on a project that needs an object that generates simple XML. I'm quite new to XML and still learning c++.
What I'm trying to achieve is a function that can be called like this in the code:

std::string xml = createXML("name", "attribute1", 10, "attribute2",
                            "stringForAttrib2");

After this line, the xml string should contain something like: < parameter name="name" attribute1=10 attribute2="stringForAttrib2" />

The function has to accept as many attributes as wanted and the name is not optional. I tried something like this:

template<typename ...Attributes>
void XMLGenerator::createXML(std::string param_name, Attributes... attributes)
{
    std::ostringstream xmlString;
    xmlString << "<parameter name=\"" << param_name << "\" ";
    createXML(xmlString, attributes...);
    xmlString << "/>";
    m_xmlString << xmlString;
}

template<typename ...Attributes, typename DataType>
void XMLGenerator::createXML(std::ostringstream &xmlString,
                             std::string attribut_name, DataType data,
                             Attributes... attributes)
{
    xmlString << attribut_name << "=\"" << data << "\" ";
    createXML(xmlString, attributes...);
}

void XMLGenerator::addParameter(std::ostringstream &xmlString){ }

g++ doesn't like it:

undefined reference to `void XMLGenerator::createXML< char const*, int, char const*, >char const*>(std::string, char const*, int, char const*, char const*)'

Why isn't the first createXML method called ?

Also if there's a simpler solution to my problem, I would be interested.

Это было полезно?

Решение

Here a solution, the forward declaration are important and note the use of std::forward combines with universal references Ts&& :

#include <iostream>
#include <string>

// =================================
// print_attribs parameter usage : ( os, name0, val0, ..., nameN, valN )
// name is char const* or std::string
// val can be anything compatible with os operator<<
// print_attribs is specialize for char const* and std::string values to enclose them with double quotes.

template < typename T, typename... Ts> void print_attribs( std::ostream & os, char const * name, T&& val, Ts&&... ts );
template < typename... Ts>             void print_attribs( std::ostream & os, char const * name, char const * val, Ts&&... ts );
template < typename... Ts>             void print_attribs( std::ostream & os, char const * name, std::string const & val, Ts&&... ts );

template < typename T, typename... Ts> void print_attribs( std::ostream & os, std::string const & name, T&& val, Ts&&... ts );
template < typename... Ts>             void print_attribs( std::ostream & os, std::string const & name, char const * val, Ts&&... ts );
template < typename... Ts>             void print_attribs( std::ostream & os, std::string const & name, std::string const & val, Ts&&... ts );

void print_attribs( std::ostream & os ) { }

template < typename... Ts>
void print_attribs( std::ostream & os, char const * name, char const * val, Ts&&... ts ) { 
    os << " " << name << "=\"" << val << "\"";
    print_attribs( os, std::forward<Ts>(ts)... );
}

template < typename... Ts>
void print_attribs( std::ostream & os, std::string const & name, char const * val, Ts&&... ts ) { 
    print_attribs( os, name.c_str(), val, std::forward<Ts>(ts)... );
}

template < typename... Ts>
void print_attribs( std::ostream & os, char const * name, std::string const & val, Ts&&... ts ) { 
    os << " " << name << "=\"" << val << "\"";
    print_attribs( os, std::forward<Ts>(ts)... );
}

template < typename... Ts>
void print_attribs( std::ostream & os, std::string const & name, std::string const & val, Ts&&... ts ) { 
    print_attribs( os, name.c_str(), val, std::forward<Ts>(ts)... );
}

template < typename T, typename... Ts>
void print_attribs( std::ostream & os, char const * name, T&& val, Ts&&... ts ) { 
    os << " " << name << "=" << std::forward<T>(val);
    print_attribs( os, std::forward<Ts>(ts)... );
}

template < typename T, typename... Ts>
void print_attribs( std::ostream & os, std::string const & name, T&& val, Ts&&... ts ) { 
    print_attribs( os, name.c_str(), std::forward<T>(val), std::forward<Ts>(ts)... );
}

template < typename... Ts>
void print( std::ostream & os, char const * name, Ts&&... ts ) {
    static_assert( sizeof...(ts) % 2 == 0, "arguments must be paired of string and value" );
    os << "<parameter name=\"" << name << "\"";
    print_attribs( os, std::forward<Ts>(ts)... );
    os << " />";
}

template < typename... Ts>
void print( std::ostream & os, std::string const & name, Ts&&... ts ) {
    static_assert( sizeof...(Ts) % 2 == 0, "Ts must be even, as they are pairs of name and value" );
    print( os, name.c_str(), std::forward<Ts>(ts)... );
}

int main() {
    auto endl = [] { std::cout << std::endl; };
    print( std::cout, "example", "foo", 10 ); endl();
    print( std::cout, "example", "foo", 10, "bar", "bare" ); endl();
    print( std::cout, "example", "foo", 10, "bar", 3.14f ); endl();
    print( std::cout, "example", "foo", 10, "bar", 3.14f, "bar", 3.14f ); endl();
    print( std::cout, "example", "foo", 10, "bar", "bare", "baz", 3.14f ); endl();
}

EDIT : Added static_assert of arguments count and char const* alternative to bypass needless std::string constructions.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top