0

I'm reading various data from a ptree. One of the fields is declared as

char * segmentCode;

So I'm reading into the segmentCode this way:

segmentCode = dataTree.get<char*>("segmentCode");

However, it throws an Access Violation. Where did I go wrong?

A workaround is ugly:

string tmpStr = dataTree.get<string>("segmentCode");    
segmentCode = strdup(tmpStr.c_str());
manlio
  • 18,345
  • 14
  • 76
  • 126
Michał Leon
  • 2,108
  • 1
  • 15
  • 15
  • 1
    My guess is that you probably return an invalid pointer in `dataTree.get` (i.e. a pointer to a local object declared inside the function, which is being invalidated at the function exit). – vsoftco May 23 '14 at 20:17
  • 1
    I had many problems doing this myself, and eventually found that reworking the problem so that I could use an `std::string` was far better. However, if you really must, read it in as an `std::string`, then call `c_str()` as you are. Then you must copy the characters in the string and assign those to your resulting pointer or it'll go out of scope. – OMGtechy May 23 '14 at 20:27
  • Thank you. I really dislike the way strings are handled in pure C, but the data exchange interface is for some reason in C, so I need to deal with char*. Sadly, it seems that my workaround is the only way to deal with a 42-year-old programming language. – Michał Leon May 24 '14 at 10:32

1 Answers1

3

What's the reasoning for using char * for segmentCode? In a C++ world the preference is to use other objects to manage associated data (e.g., std::string, std::vector<>)

If we look at the ptree.hpp header:

/**
  * A property tree with std::string for key and data, and default
  * comparison.
  */
typedef basic_ptree<std::string, std::string> ptree;

We can see that ptree uses std::string internally for both its keys and values. This implies that ptree's preferred data format is std::string.

As such the recommendation is to use:

std::string segmentCode = dataTree.get<std::string>("segmentCode");

This is more efficient than your workaround as the string should only be being copied once (from the ptree). While strdup will create yet another copy and then require an explicit call later to free.

-

Now as to where that access violation is coming from...

If we have a look at the basic_tree::get<Type>() method:

/** Shorthand for get_child(path).get_value\<Type\>(). */
template<class Type>
Type get(const path_type &path) const;

we see this calls through to basic_tree::get_value<Type>():

/** Take the value of this node and attempt to translate it to a
 * @c Type object using the default translator.
 * @throw ptree_bad_data if the conversion fails.
 */
template<class Type>
Type get_value() const;

Notice the mention of type translation and a default translator.

Further down the call stack we find the point of access violation is actually within the translation process and can be found in stream_translator.hpp (customize_stream::extract):

static void extract(std::basic_istream<Ch, Traits>& s, E& e) {
    s >> e;
    if(!s.eof()) {
        s >> std::ws;
    }
}

Using streams in this way is a common technique for generically converting between types. Especially when one of the types being converted to/from is a string.

Problem here is that a get<char*> defines the storage type (where we are putting the value) to be a char pointer. From ptree's perspective this is considered to be a complete type that can hold a value, while the translating stream considers this as a pointer to an area of memory that can be written to.

This is the equivalent of:

std::istringstream iss("value");
char * p; // uninitialized pointer
iss >> p; // write to some random memory location

and is why an access violation occurs.

Again the Type being passed to get<Type> is expected to be able to hold a complete value.

Adaptation
  • 571
  • 6
  • 4
  • Regarding using `std::string` and copying, if the OP don't modify the received string it might be possible to use a constant string reference (i.e. `const std::string&`) and the library and compiler may optimize it to be no copy at all. – Some programmer dude May 23 '14 at 20:42
  • Thank you for explaining why it doesn't work, but how about the answer to how to do it without converting to `std:string`? – Michał Leon May 24 '14 at 10:37
  • `ptree` has already converted and internally stored the parsed values as `std::string` (i.e., readxml). `get` then retrieves this value. If you want to completely avoid the use of `std::string` you'll have to use an alternative library. It's hard to recommend which without knowing what format you are parsing and why `std::string` is not a good fit. – Adaptation May 27 '14 at 17:20