c++boost-propertytree

Read a hexadecimal value starting with \x from a text file


I am sending data to a serial device using the function write() in boost::asio

In the code, I have hardcoded a string, which is detected by the serial device in hexadecimal format.

string test = "\x0C";
write(port, buffer(test.c_str(), test.size()));

However, If I read that value "\x0C" from a config file, which is in ASCII, it detects it as an ASCII value.

To read the string from the config file I am using the function boost::property_tree::xml_parser::read_xml.

read_xml(filename, pt);
command = pt.get<string>("conf.serial.command");

How could this value be treated in the same way as the other one?


Solution

  • Two approaches

    Xml Character References

    Like a commenter offered, use XML character references, as XML facilitates:

    <?xml version="1.0" encoding="utf-8"?>
    <conf>
        <serial>
            <command>&#0c;</command>
        </serial>
    </conf>
    

    When tested:

    Live On Coliru

    #include <boost/property_tree/xml_parser.hpp>
    #include <iostream>
    
    using boost::property_tree::ptree;
    
    int main() {
        ptree pt;
        {
            std::istringstream iss("<conf><serial><command>&#0c;</command></serial></conf>");
            read_xml(iss, pt);
        }
    
        std::string const command = pt.get<std::string>("conf.serial.command");
    
        std::cout << "Correct? " << std::boolalpha << (command == "\x0c") << "\n";
    }
    

    Prints

    Correct? true
    

    Use A Translator

    You have a point, you can roll your own character escapes, if you provide the code to de-escape:

    struct Escaped {
        using internal_type = std::string;
        using external_type = std::string;
    
        std::string get_value(std::string const& input) const {
            using namespace boost::spirit::qi;
            std::string result;
            parse(input.begin(), input.end(), *(
                        "\\x" >> uint_parser<uint8_t, 16, 2, 2>{}
                        | char_) , result);
            return result;
        }
    };
    

    You can then use that as a PopertyTree translator:

    Live On Coliru

    #include <boost/property_tree/xml_parser.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <iostream>
    
    struct Escaped {
        using internal_type = std::string;
        using external_type = std::string;
    
        std::string get_value(std::string const& input) const {
            using namespace boost::spirit::qi;
            std::string result;
            parse(input.begin(), input.end(), *(
                        "\\x" >> uint_parser<uint8_t, 16, 2, 2>{}
                        | char_) , result);
            return result;
        }
    };
    
    int main() {
        boost::property_tree::ptree pt;
        {
            std::istringstream iss(R"(<conf><serial><command>\x0c</command></serial></conf>)");
            read_xml(iss, pt);
        }
    
        auto command = pt.get<std::string>("conf.serial.command", Escaped{});
    
        std::cout << "Correct? " << std::boolalpha << (command == "\x0c") << "\n";
    }
    

    BONUS

    Adding put_value to the translator: Live On Coliru

    std::string put_value(std::string const& input) const {
        std::ostringstream result;
        for (uint8_t ch : input) {
            if (isprint(ch))
                result << ch;
            else
                result << "\\x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(ch);
        }
        return result.str();
    }
    

    So

    pt.put("conf.serial.command", "\x68\x65\x6c\x6c\x6f\t\x77\x6frld\n", Escaped{});
    std::cout << "New value (raw): " << pt.get<std::string>("conf.serial.command") << "\n";
    

    Prints

    New value (raw): hello\x09world\x0a