[property_tree review] Making the interface leaner, concretely...

Hi, I wanted to give this a shot, as some others have agreed that the member-interface of ptree should be simplified - to being more like that of a container. I would plainly take out all the member functions labeled as "Ptree-specific operations" (see basic_ptree in the Synopsis in the main doc page: http://kaalus.atspace.com/ptree/doc/index.html ). A first step would be to convert them into a set of non-member functions (preferably in a separate header file). Another issue is that we have a multitude of functions because orthogonal features/concepts are being mixed together: - get, optional-get, default-get, put, and set (to be added) - own-data, child, and any-path access - for paths, custom or default separators - actual conversion of a value to/from a data string (or a ptree) - features such as first-class support for indexing an array also might be desirable Also what about: - stricter error checks: what happens if two children have the same name, making a path ambiguous? what about "hybrid" record/array tree nodes? - customization points: how do I specialize the conversion of a UDT to/from a ptree ? I would like to suggest as a starting point for this decomposition, to make the ptree library more flexible/adaptable to individual needs. How about the following building blocks: ? 1) value <-> ptree conversion templates void set( const Value& v, ptree& storage ); void get( Value& v, ptree const& storage ); //throws if conv. fails bool tryget( Value& v, ptree const& storage ) //ret. false if fails { try{ get(v,storage); return true; }catch(...){} return false; } The default implementation of these functions would use lexical_cast to store/retrieve a value to/from a ptree with only an 'own' value set. The template could of course be specialized for user-defined types. For example, I could implement these functions for a struct used as a member in various locations, and convert it into a ptree with a child for each struct data member. Which of get/tryget (throwing and non-throwing functions) is implemented in terms of the other is TBD. 2) Child access (direct child only) ptree const& getField( ptree const& tree, string childName ); // gets a field, throws in case of failure or ambiguity ptree const& trygetField( ptree const& tree, string childName ); // returns empty_ptree if the field is not found or ambiguous ptree& makeField( ptree& tree, string childName ); // accesses the field, creates it if needed ( like std::map::op[] ) + Do we want an "addField" function that always creates a new child even if one already exists? Or should we provide array-indexing functions ? 3) getoptional etc Shall be defined in terms of a sequence of calls to the previous functions. Exact set to be defined as discussed elsewhere. 4) path access TBD. I'll find it acceptable if existing get/put functions are preserved as non-member functions, but left outside of the class, and maybe preferably in a sub-namespace (ptree_stringpath, or whatever). Various proposals have been discussed in these review posts, and again could be implementable in terms of the above. Comments/opinions ? Ideas for using operator overloading? Ivan -- http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form

Ivan Vecerina wrote:
Hi, I wanted to give this a shot, as some others have agreed that the member-interface of ptree should be simplified - to being more like that of a container.
I would plainly take out all the member functions labeled as "Ptree-specific operations" (see basic_ptree in the Synopsis in the main doc page: http://kaalus.atspace.com/ptree/doc/index.html ). A first step would be to convert them into a set of non-member functions (preferably in a separate header file).
Why do you think it is an advantage to make these functions non-members? They still mention the property_tree in the parameter list and so are logically still is part of the public interface of ptree. (Thus the claim about lower coupling is somewhat overstated for a library that rarely changes). IFACIT, (especially normal) users prefer members. It gives them one place to look for the interface (and is really handy in most IDE's when "." triggers a list of functions). Or we could go back to C and make everything a non-member. -Thorsten

"Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message news:444B3FDD.3000404@dezide.com... : Ivan Vecerina wrote: : > I would plainly take out all the member functions labeled as : > "Ptree-specific operations" (see basic_ptree in the Synopsis in the : > main doc page: http://kaalus.atspace.com/ptree/doc/index.html ). : > A first step would be to convert them into a set of non-member : > functions (preferably in a separate header file). : : Why do you think it is an advantage to make these functions non-members? : They still mention the property_tree in the parameter list and so are : logically still is part of the public interface of ptree. : (Thus the claim about lower coupling is somewhat overstated for a : library that rarely changes). Well, I think that I am of a school of thought defended by Scott Meyers, Herb Sutter, Andrei Alexandrescu, and others: http://www.ddj.com/dept/cpp/184401197 http://www.gotw.ca/publications/mill02.htm : IFACIT, (especially normal) users prefer members. It gives them one : place to look for the interface (and is really handy in most IDE's when : "." triggers a list of functions). Or we could go back to C and make : everything a non-member. Very concretely, here, the issue is that we heading for an std::string syndrome. Take a look at c.l.c++ and see how often users want to derive from std::string to extend its interface. std::string includes about a hundred of member functions (many of which overlap with operations available in std::algorithm). Yet it will never be enough: users look for trimming functions, case or encoding conversion functions, etc etc. Do you think that std::string is an example that ptree wants to follow? Marcin already has included a large set of useful operations as members: there are already 27 functions under "Ptree-specific operations", and Marcin has already identified members that he would like to add (e.g. 'set' as a complement to 'put'). Yet they will never satisfy a large enough user base. Some want path access with separate strings. Some want path access with '.', or '/', or any char as a separator. Some want to escape the separator. Some ask for array indexing - embedded in the string, or as a separate operation. And many users may never want to use paths ! Then there is improved support for arrays (why do I have to push_back a pair when I only should specify 'second', the array value ?), error handling policies (what happens if a path is ambiguous because multiple children are homonyms?), and more. How many member functions do we want to have in the end ? Will those be representative of the interface subset that a majority of users wants to use? Too many members are confusing too: we had a couple posts already from people who were expecting ptree::find to accept paths. I also haven't found a description of how to customize the conversion of some UDTs to ptree. This should be possible, and IMO is done more naturally with non-member templates. No matter how hard you try, you will end up having a mess of a large member-interface, yet fail to satisfy the needs of the majority. The only adequate solution I see to this problem is to provide a minimum set of member functions. Then document well a core set of primitive operations (non-members in my preference). And if we want to provide a "simple interface for dummies", put it in a sub-namespace ( ptree::path ) so those who want to use your default simple interface can include it & find it there. Regards, Ivan -- http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form

Ivan Vecerina wrote:
"Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message news:444B3FDD.3000404@dezide.com... : Ivan Vecerina wrote: : > I would plainly take out all the member functions labeled as : > "Ptree-specific operations" (see basic_ptree in the Synopsis in the : > main doc page: http://kaalus.atspace.com/ptree/doc/index.html ). : > A first step would be to convert them into a set of non-member : > functions (preferably in a separate header file). : : Why do you think it is an advantage to make these functions non-members? : They still mention the property_tree in the parameter list and so are : logically still is part of the public interface of ptree. : (Thus the claim about lower coupling is somewhat overstated for a : library that rarely changes).
Well, I think that I am of a school of thought defended by Scott Meyers, Herb Sutter, Andrei Alexandrescu, and others: http://www.ddj.com/dept/cpp/184401197 http://www.gotw.ca/publications/mill02.htm
I'm familiar with Scott's and Herb's views. However, just looking at Mayer's example struct Point { int x, y; }; vs class Point { public: int getXValue() const; int getYValue() const; void setXValue(int newXValue); void setYValue(int newYValue); private: ... // whatever... }; One of my brain-dead teachers back at universoty, showed the first as "the C way" and the second as "the superior Java way". It is certainly true that the latter is more encapsulted than the first. But it is also true that it is over encapsulated: there is no invariant to protect. Here are some other examples of classes that has been designed as over-encapsulated: - std::complex - standard containers
: IFACIT, (especially normal) users prefer members. It gives them one : place to look for the interface (and is really handy in most IDE's when : "." triggers a list of functions). Or we could go back to C and make : everything a non-member.
Very concretely, here, the issue is that we heading for an std::string syndrome. Take a look at c.l.c++ and see how often users want to derive from std::string to extend its interface. std::string includes about a hundred of member functions (many of which overlap with operations available in std::algorithm). Yet it will never be enough: users look for trimming functions, case or encoding conversion functions, etc etc.
Do you think that std::string is an example that ptree wants to follow?
No. But I don't see the analogy. A Sequence is a very common concept, but a property_tree is not. AFAICT, there's only going to be Marcin's tree.
Marcin already has included a large set of useful operations as members: there are already 27 functions under "Ptree-specific operations", and Marcin has already identified members that he would like to add (e.g. 'set' as a complement to 'put'). Yet they will never satisfy a large enough user base. Some want path access with separate strings. Some want path access with '.', or '/', or any char as a separator. Some want to escape the separator. Some ask for array indexing - embedded in the string, or as a separate operation. And many users may never want to use paths ! Then there is improved support for arrays (why do I have to push_back a pair when I only should specify 'second', the array value ?), error handling policies (what happens if a path is ambiguous because multiple children are homonyms?), and more. How many member functions do we want to have in the end ? Will those be representative of the interface subset that a majority of users wants to use?
If the free-standing functions are not generic, there is little point in not making them members. Having free-standing functions like get/set is a bad bad idea with the current rules for ADL.
Too many members are confusing too: we had a couple posts already from people who were expecting ptree::find to accept paths.
That should be explained by a better toturial.
No matter how hard you try, you will end up having a mess of a large member-interface, yet fail to satisfy the needs of the majority.
For one, I'm not suggesting XX new members. Nor am I suggesting XX new free-standing functions. But for whatever number of interface functions we find appropriate, I see no compelling reason for not making them members and at least two problems with free-standing. -Thorsten

"Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message news:444B945C.3000509@dezide.com... : I'm familiar with Scott's and Herb's views. However, just looking at : Mayer's example : : struct Point { : int x, y; : }; : : vs : : class Point { : public: : int getXValue() const; : int getYValue() const; : void setXValue(int newXValue); : void setYValue(int newYValue); : : private: : ... // whatever... : }; : : One of my brain-dead teachers back at universoty, showed the first as : "the C way" and the second as "the superior Java way". : : It is certainly true that the latter is more encapsulted than the first. : But it is also true that it is over encapsulated: there is no invariant : to protect. Exactly. And property_tree without the value i/o encapsulates all the relevant invariants. : Here are some other examples of classes that has been designed : as over-encapsulated: : : - std::complex Agreed. It was probably pointless to hide the representation as (real,imaginary) coordinates. : - standard containers In what way? : > Do you think that std::string is an example that ptree wants to follow? : : No. But I don't see the analogy. A Sequence is a very common concept, : but a property_tree is not. AFAICT, there's only going to be Marcin's : tree. The main interest of property_tree is that it provides a common gateway to a variety of file-format back-ends. In the same fashion, I think that we will have a variety of front-ends, because needs vary, and an all-encompassing solution is not provided yet. : If the free-standing functions are not generic, there is little point : in not making them members. Having free-standing functions like : get/set is a bad bad idea with the current rules for ADL. I didn't explicitly indicate that the functions were templates, but they obviously would be. If we really want to provide member functions for ease-of-use purposes, then I believe that we should make this a separate class from the ptree "container". It would be pretty straightforward to do so. Ivan -- http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form

Ivan Vecerina wrote:
"Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message
: But it is also true that it is over encapsulated: there is no invariant
: Here are some other examples of classes that has been designed : as over-encapsulated:
: - standard containers In what way?
Lack of inplace-construction or construction with uninitialized dataor or acceptance of a buffer to give to the vector. This hurts performance in libraries such as serialization or asio.
: > Do you think that std::string is an example that ptree wants to follow? : : No.
: If the free-standing functions are not generic, there is little point : in not making them members. Having free-standing functions like : get/set is a bad bad idea with the current rules for ADL.
I didn't explicitly indicate that the functions were templates, but they obviously would be.
They are templates, yes, but still mention basic_ptree.
If we really want to provide member functions for ease-of-use purposes, then I believe that we should make this a separate class from the ptree "container". It would be pretty straightforward to do so.
In the same manner operator[]() could have been left out of std::map<T>, yet is is one of the most convenient functions. -Thorsten

"Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message news:444BDD01.5030601@dezide.com... : Ivan Vecerina wrote: ... : > : Here are some other examples of classes that has been designed : > : as over-encapsulated: : : > : - standard containers : > In what way? : : Lack of inplace-construction or construction with uninitialized dataor : or acceptance of a buffer to give to the vector. : : This hurts performance in libraries such as serialization or asio. All STL containers are strict about ensuring proper construction/ initialization of their contents. I think that this is a consistent choice. Is the performance of those libraries measurably hit? If so, maybe you want a pod_vector, which will also benefit from relying on realloc. No big deal. ... : > If we really want to provide member functions for ease-of-use : > purposes, then I believe that we should make this a separate : > class from the ptree "container". : > It would be pretty straightforward to do so. : : In the same manner operator[]() could have been left out : of std::map<T>, yet is is one of the most convenient functions. I don't get the point of this comment: I gave a very clear rationale for not having op[] in std::multimap, which does not apply to std::map at all. My point remains: ptree is very adequately decoupled from file i/o parsers. Why would you refuse to provide such decoupling for the value conversions ? The current value conversions are far from providing a complete feature set. Some want arrays. Some want a serialization interface. How can you justify carrying a narrow subset of the possible conversion operations into the container itself ?? If the only justification is the desire to provide member functions because "it's the better java way", "some editors support it better", or "it will be easier for novices", then please offer a separate class that stores a reference to a ptree container: class ptree_valueio { ... there you can store a default path separator, a default locale, etc etc etc -- as some requested. ... public: ptree& tree(); ... all the current input/output functions ... }; If this is not worth the effort, then provide a non-member interface. Ivan -- http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form

Ivan Vecerina wrote:
"Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message news:444BDD01.5030601@dezide.com... : Ivan Vecerina wrote: .... : > : Here are some other examples of classes that has been designed : > : as over-encapsulated: : : > : - standard containers : > In what way? : : Lack of inplace-construction or construction with uninitialized dataor : or acceptance of a buffer to give to the vector. : : This hurts performance in libraries such as serialization or asio.
All STL containers are strict about ensuring proper construction/ initialization of their contents. I think that this is a consistent choice.
That's irrelevant.
Is the performance of those libraries measurably hit?
Yes (I havn't looked at how std::vector is used in ASIO, but I can't see how it could get around the problem).
If so, maybe you want a pod_vector, which will also benefit from relying on realloc. No big deal.
It's not just for PODs. The introduction of new types bobbles up through your interfaces, and you can't convert it to something the normal user knows without copiying the data again.
.... : > If we really want to provide member functions for ease-of-use : > purposes, then I believe that we should make this a separate : > class from the ptree "container". : > It would be pretty straightforward to do so. : : In the same manner operator[]() could have been left out : of std::map<T>, yet is is one of the most convenient functions.
I don't get the point of this comment: I gave a very clear rationale for not having op[] in std::multimap, which does not apply to std::map at all.
operator[]() is an example of a convenience function which the standard added as a member. So is the newly accepted at() member for std::map().
My point remains: ptree is very adequately decoupled from file i/o parsers. Why would you refuse to provide such decoupling for the value conversions ?
The current value conversions are far from providing a complete feature set. Some want arrays. Some want a serialization interface. How can you justify carrying a narrow subset of the possible conversion operations into the container itself ??
It remains to be seen how many user wishes that can be crammed into the interface in a reasonable manner. -Thorsten

"Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message news:444CB0DC.3040501@dezide.com... : Ivan Vecerina wrote: : > "Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message : > news:444BDD01.5030601@dezide.com... : > : Ivan Vecerina wrote: : > .... : > : > : Here are some other examples of classes that has been designed : > : > : as over-encapsulated: : > : : > : > : - standard containers : > : > In what way? : > : : > : Lack of inplace-construction or construction with uninitialized dataor : > : or acceptance of a buffer to give to the vector. : > : : > : This hurts performance in libraries such as serialization or asio. : > : > All STL containers are strict about ensuring proper construction/ : > initialization of their contents. I think that this is a consistent : > choice. : : That's irrelevant. : : > Is the performance of those libraries measurably hit? : : Yes (I havn't looked at how std::vector is used in ASIO, but I can't see : how it could get around the problem). : : > If so, maybe you want a pod_vector, which will also benefit : > from relying on realloc. No big deal. : : It's not just for PODs. How would you fill a non-POD vector with uninitialized data ? : > : > If we really want to provide member functions for ease-of-use : > : > purposes, then I believe that we should make this a separate : > : > class from the ptree "container". : > : > It would be pretty straightforward to do so. : > : : > : In the same manner operator[]() could have been left out : > : of std::map<T>, yet is is one of the most convenient functions. : > : > I don't get the point of this comment: I gave a very clear : > rationale for not having op[] in std::multimap, which does not : > apply to std::map at all. : : operator[]() is an example of a convenience function which : the standard added as a member. So is the newly accepted at() : member for std::map(). Ok. We are talking about a single member function (or two) provided for *universally* common operations. The analogy with what we are talking about is quite remote, in terms of the number of functions, of the complexity (overloads...), and sufficiency/necessity of incorporating these interfaces. : > My point remains: ptree is very adequately decoupled from file : > i/o parsers. Why would you refuse to provide such decoupling : > for the value conversions ? : > : > The current value conversions are far from providing a complete : > feature set. Some want arrays. Some want a serialization interface. : > How can you justify carrying a narrow subset of the possible : > conversion operations into the container itself ?? : : It remains to be seen how many user wishes that can be crammed : into the interface in a reasonable manner. It is exactly my point: I doubt that a sufficiently complete and consensual value & object conversion interface can be completed on time. So my suggestion is to go ahead with what we have or can do now, but make it a non-member interface to allow future evolution. I would expect that more work will follow regarding integration with boost::serialization, or other means of storing(/retrieving) objects in a ptree. Ivan -- http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form

Ivan Vecerina wrote:
"Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message news:444CB0DC.3040501@dezide.com...
: > Is the performance of those libraries measurably hit? : : Yes (I havn't looked at how std::vector is used in ASIO, but I can't see : how it could get around the problem). : : > If so, maybe you want a pod_vector, which will also benefit : > from relying on realloc. No big deal. : : It's not just for PODs.
How would you fill a non-POD vector with uninitialized data ?
If subsequent in-place construction fails, the memory needs to be removed again without calling constructors.
: It remains to be seen how many user wishes that can be crammed : into the interface in a reasonable manner.
It is exactly my point: I doubt that a sufficiently complete and consensual value & object conversion interface can be completed on time. So my suggestion is to go ahead with what we have or can do now, but make it a non-member interface to allow future evolution.
I fail to see how really disallow future evolution to have members. -Thorsten

"Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message news:444CBDBF.9010807@dezide.com... : Ivan Vecerina wrote: : > "Thorsten Ottosen" <thorsten.ottosen@dezide.com> wrote in message : > news:444CB0DC.3040501@dezide.com... : : > : > Is the performance of those libraries measurably hit? : > : : > : Yes (I havn't looked at how std::vector is used in ASIO, but I can't see : > : how it could get around the problem). : > : : > : > If so, maybe you want a pod_vector, which will also benefit : > : > from relying on realloc. No big deal. : > : : > : It's not just for PODs. : > : > How would you fill a non-POD vector with uninitialized data ? : : If subsequent in-place construction fails, the memory needs to be : removed again without calling constructors. Wow. Well I think this would have opened a Pandora box, but I don't think we need to discuss this any further. : > : It remains to be seen how many user wishes that can be crammed : > : into the interface in a reasonable manner. : > : > It is exactly my point: I doubt that a sufficiently complete : > and consensual value & object conversion interface can be : > completed on time. : > So my suggestion is to go ahead with what we have or can do now, : > but make it a non-member interface to allow future evolution. : : I fail to see how really disallow future evolution to have members. So once a better interface is found, we will add them as additional member functions, and try to avoid name collisions and misuse ? Or will we make that improved interface a non-member interface, and say "well, we have a deprecated in-class interface, but you really shouldn't use it" ? Or will you go back and remove members that were once included ? I fail to see why you reject the idea of separation of concern , which is a fundamental design principle. Besides that, the currently proposed value conversion interface is neither necessary, nor sufficient -- as demonstrated in a number of independent comments. So absolutely wanting to inject the current interface into the class "to help novices" sounds... questionable. Ivan -- http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form

Ivan Vecerina wrote:
The only adequate solution I see to this problem is to provide a minimum set of member functions. Then document well a core set of primitive operations (non-members in my preference). And if we want to provide a "simple interface for dummies", put it in a sub-namespace ( ptree::path ) so those who want to use your default simple interface can include it & find it there.
Marcin Kalicinski wrote:
I expect get() to be by far most often used function from the three (at least in my experience using the library).
I think that the path access get/put interface is what makes the code genereted by using this library clean and easy to understand. And is the principal mechanism that permitted to get a lot of functionality with only a few lines of code. If this way of accesing the data, or another with similar concept behind, is not a first part of the ptree... this container will be just another one in the world. I really like "simple interface for dummies", even more if they can fullfill my needs in a dificulty level scalable way. I feel this about the get/put actual interface of the ptree. (plus another enchacements that were proposed in the list)

Hi Ivan, I know get/put functions in general serve two orthogonal tasks: (1) access by path, and (2) type conversion. Please note that these tasks are also split in the interface: get_child() does only (1) get_own() does only (2) get() does first (1) and then (2). I expect get() to be by far most often used function from the three (at least in my experience using the library). I don't think that completely redesigning the interface by changing names and moving functions outside class will provide any extra value. The library definitely must provide the above, in this form or another. Member functions or not, it makes little difference. We must also remember that the main goal for the library is simplicity, i.e. we cannot destroy current result of three (readable) lines of code plus 1 include to get appropriately typed value from a config file. By introducing path class and splitting traits I'm going to make these tasks even more obviously independent.
1) value <-> ptree conversion templates
void set( const Value& v, ptree& storage ); void get( Value& v, ptree const& storage ); //throws if conv. fails
These functions could be made more generic by operating only on data stored in ptree. They do not need the whole tree, because they do not use paths.
The default implementation of these functions would use lexical_cast to store/retrieve a value to/from a ptree with only an 'own' value set. The template could of course be specialized for user-defined types.
You can already do that. You have to provide your own inserter/extractor, and specialize it for the types you want. Alternatively, you can provide stream operations for your types (i.e operator << and >>), in this case you do not have to do anything else.
Ambiguous keys
It might be useful in some contexts to make find function throw if there is more than one key with specified name. On the other hand, it would then be quite hard to get a value for any instance of that key. The reverse operation (i.e. find does not throw but I still want to simulate throwing behaviour) is easy. Just do that: if (pt.count(key) > 1) throw ...; I think the deciding point is "if in doubt, do as std containers do". In this case what std::multimap does. Thank you for the time you spent preparing this far reaching proposal. Looking at the practical side of things, you must be aware that implementing that, and getting all corner cases right would require months. How can you be sure that the end result would be significantly better than what we have now? I think you can do everything you want with the current version, the only difference is in syntax. Best regards, Marcin

"Marcin Kalicinski" <kalita@poczta.onet.pl> wrote in message news:e2g6hi$q6v$1@sea.gmane.org... : I know get/put functions in general serve two orthogonal tasks: (1) access : by path, and (2) type conversion. Please note that these tasks are also : split in the interface: : : get_child() does only (1) : get_own() does only (2) : get() does first (1) and then (2). The above alone brings questions: - when doing (1), I wouldn't want to pay for the support for multi-level descent using paths. I (and my teams) have used such libraries a lot, and 100% of our usage have been to go down 1 level at a time. If I never use paths, will I have to pay for multi-level paths ? [ code size, performance, and possibly restrictions on element names, maybe an additional path class ??? ] I would not want to, I hope that you can propose a design that doesn't impose this. - can I define ptree<->instance conversions for a UDT ? Let me make up a plausible example: I want to describe a graph or GUI in a config file, and in several location I use the following type to represent a position: struct Pos { int x, y; }; Obviously I do not want to read two individual fields everytime. And for readability, I want the point to be translated into a node with two values: { x: 235 y: 456 } # syntax in DataTree lib for a record Alternatively, similar types could be stored as an array of values: ( 235, 456 ) # syntax in DataTree lib for an array I want Pos to be a first-class citizen, a value that can easily be converted to/from a ptree (a ptree node) -- beacause I will be storing such value in several locations (parser back-end is irrelevant here). What exactly do I need to do to achieve this ? Can this be explained in the documentation ? Preferably, I would be using an s11n archive-like object to write once a function that can do both input and output. This doesn't have to be part of the original ptree submission, but I need to see that it could be added later without pain. : > 1) value <-> ptree conversion templates : > : > void set( const Value& v, ptree& storage ); : > void get( Value& v, ptree const& storage ); //throws if conv. fails : : These functions could be made more generic by operating only on data stored : in ptree. They do not need the whole tree, because they do not use paths. If I cannot define a node<-->struct conversion and use it the same way as a double, abstraction is killed, and ptree loses most of its interest IMO. : > The default implementation of these functions would use : > lexical_cast to store/retrieve a value to/from a ptree : > with only an 'own' value set. : > The template could of course be specialized for user-defined types. : : You can already do that. You have to provide your own inserter/extractor, : and specialize it for the types you want. Alternatively, you can provide : stream operations for your types (i.e operator << and >>), in this case you : do not have to do anything else. I would probably implement ostream << Pos (pretty-printed for debug output only). But I want to define i/o to a ptree node independently of this. : > Ambiguous keys : : It might be useful in some contexts to make find function throw if there is : more than one key with specified name. On the other hand, it would then be : quite hard to get a value for any instance of that key. The reverse : operation (i.e. find does not throw but I still want to simulate throwing : behaviour) is easy. Just do that: : : if (pt.count(key) > 1) throw ...; But if I am using paths, I would have to do this for every step along the path???? Not acceptable. : I think the deciding point is "if in doubt, do as std containers do". In : this case what std::multimap does. std::multimap does not provide operator[], because the result of this operation would potentially be ambiguous. std::multimap::find returns the first occurence of the searched key, which explicitly allows to check for subsequent items (in a sorted sequence). If you want to do "as containers do", you have to either: - allow only one entry per key (as std::map does) - do not allow direct element access if multiple values can have the same key. For me the only reasonable course of action in case of an ambiguity in a ptree path is to throw an exception / fail as if the value was not present. Or you will cause bad surprises for end-users. : Thank you for the time you spent preparing this far reaching proposal. : Looking at the practical side of things, you must be aware that implementing : that, and getting all corner cases right would require months. How can you : be sure that the end result would be significantly better than what we have : now? I think you can do everything you want with the current version, the : only difference is in syntax. Thank you Marcin for all the effort you have put in preparing this valuable submission. I find the proposed approach to offer a common denominator for xml/json/etc to be of great value in itself. However, the value<->ptree conversion part, in my opinion, is not acceptable as it is -- yet. If you provide a well-rounded, coherent, member interface that implements >=90% of needs and is cleanly extensible, I will agree with it. But for now, I find that the interface is lacking, and I think that others also expressed specific issues and disagreements with parts of the interface. I also think that others have suggested that ptree should be considered as a special kind of container, and I do not dislike the idea, because so far I have not seen a consistent and complete member interface for value i/o. The last thing I would want to have is a library where I have to juggle with an inconsistent mix of member and non-member functions. Kind regards, Ivan -- http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form
participants (4)
-
Ivan Vecerina
-
Marcin Kalicinski
-
Matias Capeletto
-
Thorsten Ottosen