Jump to page: 1 2 3
Thread overview
Open-RJ 1.3.1 released
May 23, 2005
Matthew
May 23, 2005
Rajiv Bhagwat
May 23, 2005
Matthew
May 23, 2005
Derek Parnell
May 23, 2005
Matthew
May 23, 2005
Derek Parnell
May 27, 2005
Walter
May 27, 2005
Matthew
May 23, 2005
Matthew
May 24, 2005
Rajiv Bhagwat
May 24, 2005
Rajiv Bhagwat
May 24, 2005
Matthew
May 24, 2005
Matthew
May 24, 2005
Walter
May 25, 2005
Matthew
May 24, 2005
Rajiv Bhagwat
May 26, 2005
Matthew
May 26, 2005
Rajiv Bhagwat
May 26, 2005
Walter
May 27, 2005
Rajiv Bhagwat
May 26, 2005
Walter
May 23, 2005
This is a minor update, primarily comprising the addition of record comment information representation by the API. (Previously, the comments used on records within Open-RJ database files were not available at the programmatic level.

This does not contain any write functionality that's currently underway in collaboration with Lars Ivar Igesund.

Download from http://openrj.org/ (redirects to SourceForge)

Details:

================================================================================ 23rd May 2005 : Open-RJ 1.2.1 => 1.3.1
--------------------------------------------------------------------------------

The main change is that record comments are now made available in
the API, in
the form of the 'comment' member of the ORJRecordA structure, and
the
ORJ_Record_GetCommentA() function.

There is also a new auto-link header, which causes compilers that
support such
behaviour to insert comment records that direct their linker to link
to the
appropriate Open-RJ library, e.g. openrj.vc71.mt.debug.lib, without
requiring
explicit specification in the linker command. Some developers prefer
this kind
of automatic behaviour, and now have this facility if they so wish
just by
including openrj_implicit_link.h

Other changes are as follows:

 - c_str_ptr() shims are added for ORJStringA. (See chapter 19 of
Imperfect C++
   (http://imperfectcplusplus.com/) for an explanation of the Shims
concept)
 - c_str_data() and c_str_len() shims are provided in addition to
c_str_ptr().
   These are of general use, but are particularly useful with
STLSoft's
   basic_string_view template

Changes to the mappings are as follows:

 C++ mapping:
  - the headers are separated out, one per class. They are all fully
configured,
    i.e. including openrj/cpp/filedatabase.hpp includes
openrj/cpp/record.hpp
    and openrj/cpp/field.hpp
  - addition of (in-)equality operators (operator == and operator
!=) for Field
    class
  - addition of String typedef within openrj::cpp namespace, which
is actually
    a typedef for stlsoft::basic_string_view<char> (for providing
views onto
    the string information within database fields).
  - addition of Record::GetComment(), Record::HasField(char const
*name),
    Record::HasField(char const *name, char const *value),
    Record::HasFieldWithValue(char const *value) methods
  - Record::operator []() overloads taking string objects now return
String
    rather than ORJString

 STL mapping:
  - addition of record::comment() method

 Python mapping:
  - addition of comment property to record type

 Ruby mapping:
  - addition of comment property to record type

================================================================================



May 23, 2005
Matthew,
OpenRJ seems to be such a simple spec. I wonder why the code to access it
has to be so big in size and not so evidently clear to look at. This is a
simpler spec for a simple database (actualy not even that, just a table) so
I was expecting much simpler code.

Obviously the use of templates means it is not targetted to 'c' users. To
me, the simpler use would be:
<pre>
#include "oneheader.h"
// Table taken from the original sample ..
const char *str = "%% Sample Open-RJ database - Cats and Dogs\n"
"%% Created:   28th September 2004\n"
"%% Updated:   29th September 2004\n"
"Name:  Barney\n"
"Species: Dog\n"
"%%\n"
...

"Name:  Sparky\n"
"Species: Cat\n"
"%%\n";

// Emit the table contents as specified by 'str'
void emit(const char *str){
    Table   table;
    Record  rec;
    Field   fld;

    table.read_from_memory(str);
    cout << table.comment();
    foreach(table){                      // Loop over the records of the
table
        rec = table.current();                    // grab the 'current'
record
        foreach(rec){                                  // Loop over the
fields
            fld = rec.current();                   // grab the 'current'
field
            cout << fld.name() << "= '" << fld.value() << "'" << endl; //
use!
            }
        cout << "%% " << rec.comment() << endl;
        }
    }

int main(){
    try {
        emit(str);                                              // from
memory
        }
    catch (const std::exception & e){
        cout << e.what() << endl;
        return 1;
        }
    return 0;
    }

</pre>

Any comments? Am I missing something deeper? Is there a place in OpenRj for
such a 'simpler', essentially 1 header version?
- Rajiv
PS: Yet to grab your book, but liked your DDJ articles very much. Thats why
it is painful to see complex code coming from you..


"Matthew" <admin@stlsoft.dot.dot.dot.dot.org> wrote in message news:d6rob3$cf7$1@digitaldaemon.com...
> This is a minor update, primarily comprising the addition of record comment information representation by the API. (Previously, the comments used on records within Open-RJ database files were not available at the programmatic level.
>
> This does not contain any write functionality that's currently underway in collaboration with Lars Ivar Igesund.
>
> Download from http://openrj.org/ (redirects to SourceForge)
>
> Details:
>
>
============================================================================ ====
> 23rd May 2005 : Open-RJ 1.2.1 => 1.3.1
> --------------------------------------------------------------------------
------
>
> The main change is that record comments are now made available in
> the API, in
> the form of the 'comment' member of the ORJRecordA structure, and
> the
> ORJ_Record_GetCommentA() function.
>
> There is also a new auto-link header, which causes compilers that
> support such
> behaviour to insert comment records that direct their linker to link
> to the
> appropriate Open-RJ library, e.g. openrj.vc71.mt.debug.lib, without
> requiring
> explicit specification in the linker command. Some developers prefer
> this kind
> of automatic behaviour, and now have this facility if they so wish
> just by
> including openrj_implicit_link.h
>
> Other changes are as follows:
>
>  - c_str_ptr() shims are added for ORJStringA. (See chapter 19 of
> Imperfect C++
>    (http://imperfectcplusplus.com/) for an explanation of the Shims
> concept)
>  - c_str_data() and c_str_len() shims are provided in addition to
> c_str_ptr().
>    These are of general use, but are particularly useful with
> STLSoft's
>    basic_string_view template
>
> Changes to the mappings are as follows:
>
>  C++ mapping:
>   - the headers are separated out, one per class. They are all fully
> configured,
>     i.e. including openrj/cpp/filedatabase.hpp includes
> openrj/cpp/record.hpp
>     and openrj/cpp/field.hpp
>   - addition of (in-)equality operators (operator == and operator
> !=) for Field
>     class
>   - addition of String typedef within openrj::cpp namespace, which
> is actually
>     a typedef for stlsoft::basic_string_view<char> (for providing
> views onto
>     the string information within database fields).
>   - addition of Record::GetComment(), Record::HasField(char const
> *name),
>     Record::HasField(char const *name, char const *value),
>     Record::HasFieldWithValue(char const *value) methods
>   - Record::operator []() overloads taking string objects now return
> String
>     rather than ORJString
>
>  STL mapping:
>   - addition of record::comment() method
>
>  Python mapping:
>   - addition of comment property to record type
>
>  Ruby mapping:
>   - addition of comment property to record type
>
>
============================================================================ ====
>
>
>


May 23, 2005
"Rajiv Bhagwat" <dataflow@vsnl.com> wrote in message news:d6stkt$1ee6$1@digitaldaemon.com...
> Matthew,
> OpenRJ seems to be such a simple spec.

It is

> I wonder why the code to access it
> has to be so big in size and not so evidently clear to look at.

What's big?

The source files are small

23/05/2005  10:52:26 AM  R----A--      38949
H:\freelibs\openrj\1.3.x\src\orjapi.c
18/02/2005  02:04:51 PM  R----A--       3319
H:\freelibs\openrj\1.3.x\src\orjmem.c
11/04/2005  11:12:53 PM  R----A--       6223
H:\freelibs\openrj\1.3.x\src\orjstr.c
23/05/2005  12:29:20 PM  R----A--      30756
H:\freelibs\openrj\1.3.x\include\openrj\openrj.h
08/04/2005  04:34:24 PM  R----A--       3285
H:\freelibs\openrj\1.3.x\include\openrj\openrj_assert.h
08/04/2005  04:47:59 PM  R----A--       5543
H:\freelibs\openrj\1.3.x\include\openrj\openrj_implicit_link.h
08/04/2005  04:34:30 PM  R----A--       3399
H:\freelibs\openrj\1.3.x\include\openrj\openrj_memory.h

Effectively everything is declared within openrj.h, and implemented within orjapi.c.

It compiles to ~10K.

> This is a
> simpler spec for a simple database (actualy not even that, just a
> table) so
> I was expecting much simpler code.

Maybe you're mistaking the extent of the code for the other mappings - C++, Ch, D, .NET, Python, Ruby, STL, etc. - with the base API?

> Obviously the use of templates means it is not targetted to 'c'
> users. To
> me, the simpler use would be:
> <pre>
> #include "oneheader.h"
> // Table taken from the original sample ..
> const char *str = "%% Sample Open-RJ database - Cats and Dogs\n"
> "%% Created:   28th September 2004\n"
> "%% Updated:   29th September 2004\n"
> "Name:  Barney\n"
> "Species: Dog\n"
> "%%\n"
> ...
>
> "Name:  Sparky\n"
> "Species: Cat\n"
> "%%\n";
>
> // Emit the table contents as specified by 'str'
> void emit(const char *str){
>    Table   table;
>    Record  rec;
>    Field   fld;
>
>    table.read_from_memory(str);
>    cout << table.comment();
>    foreach(table){                      // Loop over the records
> of the
> table
>        rec = table.current();                    // grab the
> 'current'
> record
>        foreach(rec){                                  // Loop over
> the
> fields
>            fld = rec.current();                   // grab the
> 'current'
> field
>            cout << fld.name() << "= '" << fld.value() << "'" <<
> endl; //
> use!
>            }
>        cout << "%% " << rec.comment() << endl;
>        }
>    }
>
> int main(){
>    try {
>        emit(str);                                              //
> from
> memory
>        }
>    catch (const std::exception & e){
>        cout << e.what() << endl;
>        return 1;
>        }
>    return 0;
>    }
>
> </pre>
>
> Any comments? Am I missing something deeper? Is there a place in
> OpenRj for
> such a 'simpler', essentially 1 header version?

I don't really get your point. It pretty much does that.

I've attached a fully functioning program adapted from your example code. The only Open-RJ #include it requires is

    #include <openrj/stl/database.hpp>

Can you explain where the program fails to satisfy your (and my!) desire for simplicity?

> - Rajiv
> PS: Yet to grab your book, but liked your DDJ articles very much.

Thanks.

> Thats why
> it is painful to see complex code coming from you..

Ok. I guess I see complexity as something different. I'm keen to hear more, though, about what/why you think it's complex.

Cheers

Matthew





May 23, 2005
On Tue, 24 May 2005 08:00:29 +1000, Matthew wrote:

> // This sample adapted from a newsgroup post by Rajiv Bhagwat
> 
> #include <openrj/stl/database.hpp>
> //#include <openrj/openrj_implicit_link.h> // Enable this to avoid the need to specify the lib name to the compiler
> #include <iostream>
> 
> // Table taken from the original sample ..
> const char contents[] = "%% Sample Open-RJ database - Cats and Dogs\n"
> "%% Created:   28th September 2004\n"
> "%% Updated:   24th May 2005\n"
> "Name:  Barney\n"
> "Species: Dog\n"
> "%%\n"
> "Name:  Sparky\n"
> "Species: Cat\n"
> "%%\n";
> 
> // Emit the table contents as specified by 'str'
> void emit(const char *contents)
> {
>     using namespace openrj::stl;
> 
>     memory_database db(contents);
> 
>     for(memory_database::const_iterator ri = db.begin(); ri != db.end(); ++ri) // Loop over the records
>     {
>         std::cout << (*ri).comment() << std::endl;
> 
>         for(record::const_iterator fi = (*ri).begin(); fi != (*ri).end(); ++fi) // Loop over the fields
>         {
>             std::cout << (*fi).name() << "= '" << (*fi).value() << "'" << std::endl;
>         }
>         std::cout << std::endl;
>    }
> }
> 
> int main()
> {
>    try
>    {
>        emit(contents);  // from memory
>     }
>     catch (const std::exception & e)
>     {
>         std::cout << e.what() << std::endl;
> 
>         return 1;
>     }
>    return 0;
> }

Why does C++ code look so much like the keyboard threw up ;-)

-- 
Derek Parnell
Melbourne, Australia
24/05/2005 8:14:23 AM
May 23, 2005
The previous one used the STL mapping. Here's one using the C++ mapping.

I'm not being a smart-arse. I really don't see where the complexity is, and I'm keen to understand your position.

Cheers

Matthew



May 23, 2005
Well, I was being succinct, to address Rajiv's point.

In fact, I'd never write it like that, for the very discoverability reasons your raising. I'd do it like

   for(memory_database::const_iterator ri = db.begin(); ri !=
db.end(); ++ri) // Loop over the records
   {
       record    r(*ri);

       std::cout << r.comment() << std::endl;

       for(record::const_iterator fi = r.begin(); fi != r.end();
++fi) // Loop over the fields
      {
           field f(*fi);

           std::cout << f.name() << "= '" << f.value() << "'" <<
std::endl;
       }
       std::cout << std::endl;
   }

And I'd respectfully suggest that it would be churlish to suggest that that's not easy to read.

Of course, that is from the perspective of C++ programmers. D programmers can use the Open-RJ/D mapping, which would look something like:

foreach(Record r; db)
{
  // do stuff with r

  foreach(Field f; r)
  {
    // do stuff with f
  }
}






"Derek Parnell" <derek@psych.ward> wrote in message news:1snue2euqja03.4t1llgvusnhu$.dlg@40tude.net...
> On Tue, 24 May 2005 08:00:29 +1000, Matthew wrote:
>
>> // This sample adapted from a newsgroup post by Rajiv Bhagwat
>>
>> #include <openrj/stl/database.hpp>
>> //#include <openrj/openrj_implicit_link.h> // Enable this to
>> avoid the need to specify the lib name to the compiler
>> #include <iostream>
>>
>> // Table taken from the original sample ..
>> const char contents[] = "%% Sample Open-RJ database - Cats and
>> Dogs\n"
>> "%% Created:   28th September 2004\n"
>> "%% Updated:   24th May 2005\n"
>> "Name:  Barney\n"
>> "Species: Dog\n"
>> "%%\n"
>> "Name:  Sparky\n"
>> "Species: Cat\n"
>> "%%\n";
>>
>> // Emit the table contents as specified by 'str'
>> void emit(const char *contents)
>> {
>>     using namespace openrj::stl;
>>
>>     memory_database db(contents);
>>
>>     for(memory_database::const_iterator ri = db.begin(); ri !=
>> db.end(); ++ri) // Loop over the records
>>     {
>>         std::cout << (*ri).comment() << std::endl;
>>
>>         for(record::const_iterator fi = (*ri).begin(); fi !=
>> (*ri).end(); ++fi) // Loop over the fields
>>         {
>>             std::cout << (*fi).name() << "= '" << (*fi).value()
>> << "'" << std::endl;
>>         }
>>         std::cout << std::endl;
>>    }
>> }
>>
>> int main()
>> {
>>    try
>>    {
>>        emit(contents);  // from memory
>>     }
>>     catch (const std::exception & e)
>>     {
>>         std::cout << e.what() << std::endl;
>>
>>         return 1;
>>     }
>>    return 0;
>> }
>
> Why does C++ code look so much like the keyboard threw up ;-)
>
> -- 
> Derek Parnell
> Melbourne, Australia
> 24/05/2005 8:14:23 AM


May 23, 2005
On Tue, 24 May 2005 08:30:41 +1000, Matthew wrote:

> Well, I was being succinct, to address Rajiv's point.
> 
> In fact, I'd never write it like that, for the very discoverability reasons your raising. I'd do it like
> 
>    for(memory_database::const_iterator ri = db.begin(); ri !=
> db.end(); ++ri) // Loop over the records
>    {
>        record    r(*ri);
> 
>        std::cout << r.comment() << std::endl;
> 
>        for(record::const_iterator fi = r.begin(); fi != r.end();
> ++fi) // Loop over the fields
>       {
>            field f(*fi);
> 
>            std::cout << f.name() << "= '" << f.value() << "'" <<
> std::endl;
>        }
>        std::cout << std::endl;
>    }
> 
> And I'd respectfully suggest that it would be churlish to suggest that that's not easy to read.


LOL!!! I be churlish then.

> Of course, that is from the perspective of C++ programmers. D programmers can use the Open-RJ/D mapping, which would look something like:
> 
> foreach(Record r; db)
> {
>   // do stuff with r
> 
>   foreach(Field f; r)
>   {
>     // do stuff with f
>   }
> }

Now *that!* is legible. ;-)

-- 
Derek
Melbourne, Australia
24/05/2005 9:39:56 AM
May 24, 2005
"Matthew" <admin@stlsoft.dot.dot.dot.dot.org> wrote in message news:d6tl85$27ok$1@digitaldaemon.com...
> The previous one used the STL mapping. Here's one using the C++ mapping.
>
> I'm not being a smart-arse. I really don't see where the complexity is, and I'm keen to understand your position.
>

Matthew, I have a lot of respect for the way you have analysed things, that's why I dared to point out the simplicity issues to you. With others, I would just let it pass.

No, I am not confusing with the other language mappings: I am talking about
the basic code.
The code which I sent you compiles as is and runs (with an alternate
implementation -  before I put my foot in the mouth, I had to come up with
the so called 'simpler' solution).

I am pointing out several things which need attention:
(Btw: this could be a wrong newsgroup for such analysis, but I believe most
of the readers would benefit. I have learned a lot of things from such
comments.) I am outlining the thought train when people look for the use of
third party source:

1. When I saw something like:
static ORJRC    ORJ_ExpandBlockAndParseA_(  ORJDatabaseA                *db
                                        ,   const size_t
cbDbStruct
                                        ,   size_t
cbData
                                        ,   const size_t
cbAll
                                        ,   unsigned
flags
                                        ,   IORJAllocator
*ator
                                        ,   ORJDatabaseA const
**pdatabase
                                        ,   ORJError
*error
                                        ,   size_t
size);

the first thought that crossed the mind: 'ohmygosh! whats this?'. I would have reservations about using such functions, however proven, along with my code. The mere length of the function makes it non-obvious.

Do we need all such allocations and reallocations? Can't we really use simpler STL constructs? After all, we want to handle a small table with less than 100 records, anything more and we will use Sqlite or Mysql.

2. Ok, I said, maybe all that is well tested. How to use it? In the test directory, the C program is 450 lines, the c++ one is 475, STL more than 500. Not counting the test table of around 25 lines, it is still quite a bit. The examples have to be much smaller, skimpier. If found suitable at the first sight, the programmers won't mind going thru the code & the documentation (in that order!) to see how to adopt it to more complex use.

Do we have to include this one every program? I wondered.
#include <openrj/cpp/openrj.hpp>
#include <openrj/cpp/field.hpp>
#include <openrj/cpp/record.hpp>
#include <openrj/cpp/database.hpp>

..
using std::cerr;
using std::endl;
using std::cout;
#if !defined(ORJ_NO_NAMESPACE)
using openrj::ORJRC;
#endif /* !ORJ_NO_NAMESPACE */
using openrj::cpp::FileDatabase;
using openrj::cpp::MemoryDatabase;
using openrj::cpp::DatabaseException;
using openrj::cpp::Field;
using openrj::cpp::Record;

Thats why my comment:
#include "oneheader.h"
and it should handle all the required includes and namespaces. Your later
examples do have the use of the single header, but we the potential users of
the library, would look at the provided test programs. A simple, quick one
and another more exhaustive one for deeper understanding would help.

If the library uses STL, why have a C program? Initially it gave the impression that C only implementations can use this library.

3. The test code uses something like 'catch(DatabaseException &x)' - why can't these people use the standard std::exceptions ? Is this one derived from that? Does it mean I have to handle this AND the standard exceptions? No immediate answers. What if I am writing a throw-away program and don't want all the exception handling?

On top of this, this guy keeps on confusing Databases with Tables everywhere... Unless the spec will be enhanced, why is he calling a Table as a Database? Aren't fields, records, tables and databases clear to everyone?? Still stuck with DBF?

Oh - later on there seems to be a 'real' main, and it shows std::exception
and catch(...). Is he expecting other exceptions?
Matthew, we look for simpler 'demo' programs...

4. What parts are 'Wizard-generated' for this program?? Would that not be a
real, usable demo program, if it uses OpenRJ?
And where is 'execute_unittest();' - its not in this file.. Will this
compile then?
What's a 'goto' doing here??? Do I trust this code? Are there any in the
base library?


5. I see the use of:
    memory_database db(contents);

    for(memory_database::c......
That means there must be a separate class for 'file_database'... Why two? Is
there a difference in the interfaces of the two classes? What if one is
ammended and the other one is not? Also, in case I have to switch over from
one to other, do I need to edit every use? After all, the name is used in
every 'for' ...

6. Matthew had a great article about iterating over arrays & containers in
many languages.. (thats all I remember now!) but why is he still using:
    memory_database db(contents);
    for(memory_database::const_iterator ri = db.begin(); ri != db.end();
++ri) // Loop over the records

followed by - (*ri).comment()
Yes, with a lot of code again and again using 'begin' and 'end' it looks
'natural' - but how come he is not tired of typing the whole thing again and
again and inventing 'ri' and 'fi', i1 and i2 and so on... How come he hasn't
stumbled upon simpler 'foreach' which hides these things? (Does he love
typing? Not worried about Carpel Tunnel?)
foreach == begin..end construct
current= (*ri)


Etc, etc. This was a typical line of thinking. I have also typed much more than reqd, surely, sorry. Am not looking for real, line by line answers, got them (it compiling to around 10k was also not obvious, it is nice to know.) This was wondering aloud. My only expectation is a good, prolific programmer like you should keep much more simpler users in mind. We all will benefit from slimmer software with simpler examples.

Thanks and regards,
- Rajiv

PS: Do I leave the implementation of the simpler version to diligent readers? My Field and Record classes are almost empty, the Table class has 3 routines to speak of, 2 of which handle using the memory and the file. All within one header. Read the example earlier, it compiles and runs.

> Cheers
>
> Matthew
>
>
>


May 24, 2005
And yes, I am talking (and regularly use) 'foreach' for c++. I have not seen
it elsewhere, but I could be (easily) wrong.. Just wondered after
deliberating over iterating and writing an article about it, how come you
still use the longer, inflexible begin()..end() form? What makes you choose
that one over others? (You are saying that 'foreach' is for D guys!)

- Rajiv


May 24, 2005
Wow! A lot to think about, and a lot of very useful feedback that's already got me thinking.

Even though you say at the end you don't expect it, I have answered most points, partly to show you where your feedback has really got me thinking, and probably changing some of my ways of doing things.

> No, I am not confusing with the other language mappings: I am talking about
> the basic code.
> The code which I sent you compiles as is and runs (with an alternate
> implementation -  before I put my foot in the mouth, I had to come up with
> the so called 'simpler' solution).
>
> I am pointing out several things which need attention:
> (Btw: this could be a wrong newsgroup for such analysis, but I believe most
> of the readers would benefit. I have learned a lot of things from such
> comments.) I am outlining the thought train when people look for the use of
> third party source:
>
> 1. When I saw something like:
> static ORJRC    ORJ_ExpandBlockAndParseA_(  ORJDatabaseA                *db
>                                        ,   const size_t
> cbDbStruct
>                                        ,   size_t
> cbData
>                                        ,   const size_t
> cbAll
>                                        ,   unsigned
> flags
>                                        ,   IORJAllocator
> *ator
>                                        ,   ORJDatabaseA const
> **pdatabase
>                                        ,   ORJError
> *error
>                                        ,   size_t
> size);
>
> the first thought that crossed the mind: 'ohmygosh! whats this?'. I would have reservations about using such functions, however proven, along with my code. The mere length of the function makes it non-obvious.
>
> Do we need all such allocations and reallocations? Can't we really use simpler STL constructs? After all, we want to handle a small table with less than 100 records, anything more and we will use Sqlite or Mysql.

First, although I like STL and use it a lot, I think libraries benefit from being written in C wherever possible and practicable. Since Open-RJ is a pretty straightforward (or I thought it was! <g>) thing, and small, I thought a C implementation appropriate.

As to that function, it's a worker function, that's only used inside the implementation. The reason it entered the codebase was when I added support for memory databases along with file databases. I preferred to keep as much code as possible common between the parsing of the memory and file databases for reasons of maintainability.

The reason that it does reallocation is that I am pathologically opposed to inefficiency, and so Open-RJ only makes two allocations, resulting in the one block. Naturally I accept that this is likely unnecessary on performance grounds for Open-RJ, but it does have the nice side-effect that closing a database is simply a case of one call to free().

The reason that an allocator may be specified is that I am also a big flexibility fan. Again, this might be largely moot, but when one's writing an open-source library that will be mapped to many languages, as few assumptions as possible is the best.

In summary, I've good reasons for the decisions made, although I'm _not_ saying that they're necessarily optimal.

> 2. Ok, I said, maybe all that is well tested. How to use it? In the test directory, the C program is 450 lines, the c++ one is 475, STL more than 500. Not counting the test table of around 25 lines, it is still quite a bit. The examples have to be much smaller, skimpier. If found suitable at the first sight, the programmers won't mind going thru the code & the documentation (in that order!) to see how to adopt it to more complex use.
>
> Do we have to include this one every program? I wondered.
> #include <openrj/cpp/openrj.hpp>
> #include <openrj/cpp/field.hpp>
> #include <openrj/cpp/record.hpp>
> #include <openrj/cpp/database.hpp>
>
> ..
> using std::cerr;
> using std::endl;
> using std::cout;
> #if !defined(ORJ_NO_NAMESPACE)
> using openrj::ORJRC;
> #endif /* !ORJ_NO_NAMESPACE */
> using openrj::cpp::FileDatabase;
> using openrj::cpp::MemoryDatabase;
> using openrj::cpp::DatabaseException;
> using openrj::cpp::Field;
> using openrj::cpp::Record;
>
> Thats why my comment:
> #include "oneheader.h"
> and it should handle all the required includes and namespaces. Your later
> examples do have the use of the single header, but we the potential users of
> the library, would look at the provided test programs. A simple, quick one
> and another more exhaustive one for deeper understanding would help.

You're 100% correct on this.

I avoid using directives like the plague, and I believe there are very good reasons for doing so in production code, but I can (now) see that for pedagogical purposes it's not exactly helpful. I'm so chagrined I'm changing this right now! :-)

> If the library uses STL, why have a C program? Initially it gave the impression that C only implementations can use this library.

Here I disagree/diverge. The library is C, and has C-API. And it has mappings to other languages. So it's appropriate to have a C test program, and to have test programs in other languages.

Nonetheless, I'm keen to hear how the (mis-)impression is given. If you can spare further advice on this issue, I'll definitely listen.

> 3. The test code uses something like 'catch(DatabaseException &x)' - why can't these people use the standard std::exceptions ? Is this one derived from that? Does it mean I have to handle this AND the standard exceptions? No immediate answers. What if I am writing a throw-away program and don't want all the exception handling?

Fair enough. I guess I wanted to show the full functionality. I don't think this is black-and-white, so maybe the answer is to have separate examples - a minimal example and a full-functionality example?

> On top of this, this guy keeps on confusing Databases with Tables everywhere... Unless the spec will be enhanced, why is he calling a Table as a Database? Aren't fields, records, tables and databases clear to everyone?? Still stuck with DBF?

The records in a database don't have to have the same structure, so (I don't think) it's reasonable to call it a table (which would imply uniformity of structure).

Convinced? (Maybe I should have documented that somewhere ...)

> Oh - later on there seems to be a 'real' main, and it shows std::exception
> and catch(...). Is he expecting other exceptions?
> Matthew, we look for simpler 'demo' programs...

Again, the separation of main_() and main() is an artefact of real applications, rather than something helpful in an example program, that's crept in as a result of habit. Maybe I'll trim that out.

> 4. What parts are 'Wizard-generated' for this program??

The skeleton. Not useful information for potential users. It's gone.

>Would that not be a
> real, usable demo program, if it uses OpenRJ?
> And where is 'execute_unittest();' - its not in this file.. Will this
> compile then?

Erm, vestigial/unfinished. Getting fixed now ...

> What's a 'goto' doing here??? Do I trust this code? Are there any in the base library?

He he. Point taken. Going, going, gone ...

> 5. I see the use of:
>    memory_database db(contents);
>
>    for(memory_database::c......
> That means there must be a separate class for 'file_database'... Why two?

One for files one for memory, both derived from a common base which has the 'main' interface, i.e. access to records and fields.

> Is there a difference in the interfaces of the two classes? What if one is ammended and the other one is not? Also, in case I have to switch over from one to other, do I need to edit every use? After all, the name is used in every 'for' ...

I disagree here, as far as I follow your argument. The common aspects of the two classes are in the base class. The derived classes merely differ in their constructors, as appropriate to the source of the database. I still think that's the best design choice.

> 6. Matthew had a great article about iterating over arrays & containers in
> many languages.. (thats all I remember now!) but why is he still using:
>    memory_database db(contents);
>    for(memory_database::const_iterator ri = db.begin(); ri != db.end();
> ++ri) // Loop over the records
>
> followed by - (*ri).comment()
> Yes, with a lot of code again and again using 'begin' and 'end' it looks
> 'natural' - but how come he is not tired of typing the whole thing again and
> again and inventing 'ri' and 'fi', i1 and i2 and so on... How come he hasn't
> stumbled upon simpler 'foreach' which hides these things? (Does he love
> typing? Not worried about Carpel Tunnel?)
> foreach == begin..end construct
> current= (*ri)

Well, this one's the desire not to prescribe that users of one library should have to use another, or should have to learn concepts that are not (yet) accepted into the mainstream of C++ programming practice. I still think that's the right choice.

> Etc, etc. This was a typical line of thinking. I have also typed much more than reqd, surely, sorry.

No apologies. Indeed, I'm very grateful for the input, and will change my libs in all those areas you've pointed out that I agree on. Thanks!

> Am not looking for real, line by line answers, got
> them (it compiling to around 10k was also not obvious, it is nice to know.)

Maybe I should mention that somewhere in the docs.

> This was wondering aloud. My only expectation is a good, prolific programmer like you should keep much more simpler users in mind. We all will benefit from slimmer software with simpler examples.

Yes, I agree. Again, thanks.

I'm touched by your sentiments, albeit I _know_ that I'm a good engineer, and a half-decent author. What I'm not good at, as I know all too well, is documentation and so I'm very grateful for your feedback. Please feel free to make as much similar feedback about Open-RJ (and recls and STLSoft) as you are able in future.

> Thanks and regards,
> - Rajiv
>
> PS: Do I leave the implementation of the simpler version to diligent readers? My Field and Record classes are almost empty, the Table class has 3 routines to speak of, 2 of which handle using the memory and the file. All within one header. Read the example earlier, it compiles and runs.

I'll happily take a look at it, if you want to post/send it to me.



« First   ‹ Prev
1 2 3