Errors with returning datatypes other than char* f

2019-08-01 17:26发布

问题:

It's my first time using XML and I am currently trying to return an integer (actually want to return a double but haven't got that far yet) from an XML-file using C++. I'm using RAPIDXML and the following implementation:

All files are in the same directory.

XML (firstxml.xml):

<?xml version="1.0"?>
<test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:noNamespaceSchemaLocation="firstxsd.xsd">
    <A>10</A>
    <B>Hello</B>
</test>

XML-Schema (firstxsd.xsd):

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="test">
    <xs:complexType>
      <xs:sequence>
        <xs:element type="xs:integer" name="A"/>
        <xs:element type="xs:string" name="B"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

C++ (test.cxx):

#include <iostream>
#include <sstream>
#include <fstream>
#include "rapidxml-1.13/rapidxml.hpp"
#include "rapidxml-1.13/rapidxml_print.hpp"
#include <string>
#include <stdio.h>
#include <vector>

int main(int argc, char* argv[])
  {
    std::ifstream file ("firstxml.xml");
      if (file.is_open())
        {
          file.seekg(0,std::ios::end);
          int size = file.tellg();
          file.seekg(0,std::ios::beg);

          char* buffer = new char [size];

          file.read (buffer, size);
          file.close();

          rapidxml::xml_document<> doc;
          doc.parse<0>(buffer);
          rapidxml::xml_node<> *node = doc.first_node()->first_node();

          //Line which results in error
          std::cout << node->value()*10 << std::endl;

          delete[] buffer;
        }
  }

The error:

test.cxx:52:31: error: invalid operands of types ‘char*’ and ‘int’ to binary ‘operator*’

From the tutorials I have read online, I believe I am constructing the files correctly and so the value parsed into the C++ file from the node A should be an integer. One thing I did notice is that in the RAPIDXML manual the specification of value() is as follows:

Ch* value() const;

Description: Gets value of node. Interpretation of value depends on type of node. Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. Use value_size() function to determine length of the value.

Returns: Value of node, or empty string if node has no value.

So the function definition says it always returns a character pointer, but the line "Interpretation of value depends on type of node" implies it returns a type-dependant value.

Thank you for taking the time to look at my issue, any help is greatly appreciated,

Paul.

回答1:

It is hard to say what does the phrase "Interpretation of value depends on type of node" mean. If the function returns Ch pointer it will always return such pointer or cause a runtime exception, if library developers provided such feature. Naturally, when you try to multiply a Ch* by an int you get compile time error.

The only way a function can return a type which depends on call context is to use some special user-defined return type (VARIANT or something like it). It seems to me that Ch is just a pseudonim for the basic character type of the library (char or maybe wchar_t).

So by that "interpretation..." notice developers probably meant that the user must make a proper interpretation of the character string in accordance to the type of node (which in turn depends on the user-defined schema).

To convert a C-string representation of a number to corresponding types you may use functions from the standard C library, such as atoi() (converts to int), atof() (to double) and so on.



回答2:

I guess you want to access "attribute" value, so do something like ...

cout << "Name of my first node is: " << doc.first_node()->name() << "\n";

xml_node<> *node = doc.first_node("foobar");

cout << "Node foobar has value " << node->value() << "\n";

for (xml_attribute<> *attr = node->first_attribute(); attr; attr = attr->next_attribute())

{ cout << "Node foobar has attribute " << attr->name() << " ";

cout << "with value " << attr->value() << "\n";

}

And also have a look at rapidXML tutorial...

http://rapidxml.sourceforge.net/manual.html its easy!



回答3:

Someone correct me if I'm wrong, I believe RapidXML will only give you values as some form of string due to its in-place parsing nature. The class templating might be for handling char vs wide char or something like that, I don't know, I only use char.

What I did to handle extracting values was to add a helper class to rapidxml_utils.hpp (could really have put it anywhere but it made sense there) with some static functions to parse a xml node or attribute to a value (or a default value if it doesn't parse).

These helpers have allowed me to do stuff like:

    int Height = xml_helper::GetNodeInt(cur_node->first_node("Height"), 48);
    int Width = xml_helper::GetNodeInt(cur_node->first_attribute("Width"), 128);

In your case you might use my helper class like this:

      //Line which results in error (altered to use my helper class, if node absent or not parse as integer uses zero)
      std::cout << xml_helper::GetNodeInt(node, 0)*10 << std::endl;

My helper class looks like this (I used sscanf to get some level of validation):

class xml_helper
{
public:
    // a couple little helpers, lets you do this:
    // int Height = xml_helper::GetNodeInt(cur_node->first_node("Height"), 48);
    // int Width = xml_helper::GetNodeInt(cur_node->first_node("Width"), 128);
    static int GetNodeInt(rapidxml::xml_base<>* node, int DefaultValue)
    {
        int temp;
        try
        {
            if (node == 0) return DefaultValue;
            if (sscanf(node->value(), "%d", &temp) != 1) return DefaultValue;
            return temp;
            //return atoi(node->value());
        }
        catch (...) { return DefaultValue; }
    }

    static unsigned int GetNodeUInt(rapidxml::xml_base<>* node, unsigned int DefaultValue)
    {
        unsigned int temp;
        try
        {
            if (node == 0) return DefaultValue;
            if (sscanf(node->value(), "%u", &temp) != 1) return DefaultValue;
            return temp;
            //return strtoul(node->value(), NULL, 0);
        }
        catch (...) { return DefaultValue; }
    }

    static long long GetNodeLL(rapidxml::xml_base<>* node, long long DefaultValue)
    {
        long long temp;
        try
        {
            if (node == 0) return DefaultValue;
            if (sscanf(node->value(), "%lld", &temp) != 1) return DefaultValue; //C99 covers %lld
            return temp;
            //return strtoll(node->value(), NULL, 0); //C++11, could use _strtoi64 in VS until then?
        }
        catch (...) { return DefaultValue; }
    }

    static double GetNodeDouble(rapidxml::xml_base<>* node, double DefaultValue)
    {
        double temp;
        try
        {
            if (node == 0) return DefaultValue;
            if (sscanf(node->value(), "%lf", &temp) != 1) return DefaultValue; // could use strtod
            return temp;
        }
        catch (...) { return DefaultValue; }
    }

    static std::string GetNodeStr(rapidxml::xml_base<>* node, std::string DefaultValue)
    {
        try
        {
            if (node == 0) return DefaultValue;
            return node->value();
        }
        catch (...) { return DefaultValue; }
    }
};

Hopefully this may be helpful to you or someone else using rapidxml and needing a quick way to get integers, doubles, or whatever out of nodes. It can certainly be expanded to other types, I only have types I've needed so far in there now, but you get the gist of it.