1

I have a class containing many members of a simple class type. More importantly, their number is growing as I go on with the development.

I need to be able to reset them all at once, and I'd like to do it without copy-pasting them. The code currently looks like:

typedef auto_ptr<odbc::PreparedStatement> Stmt;

class DbProxy {
private:
  void reset();

  Stmt a;
  Stmt b;
  Stmt c;
  // ... about 10 more
};

void DbProxy::reset()
{
  a.reset();
  b.reset();
  c.reset();
  // etc.
}

Obviously I don't like having to add every new member to the reset() function (just had a seg. fault for forgetting one).

What I intend to do is to collect them all into a structure and to allocate this structure under auto_ptr. So the code would look like this:

typedef auto_ptr<odbc::PreparedStatement> Stmt;

class DbProxy {
public:
  DbProxy(): stmts(new OdbcResources) {}
private:
  void reset() { stmts.reset(); }

  struct OdbcResources {
    Stmt a;
    Stmt b;
    Stmt c;
    // ... about 10 more
  };
  auto_ptr<OdbcResources> stmts;
};

Objects of DbProxy are not intended to be copied or copy-constructed, although I didn't bother to ensure this by making the assignment and copy-ctor private.

Do you see any problem with this approach? Do you have other suggestions?

EDIT

based on @DeadMG suggestion, what about this:

class DbProxy {
public:
  DbProxy();
private:
  enum Statements { SELECT1, SELECT2, INSERT, LAST };  // never mind the names

  void reset() { for (int i=0; i < LAST; i++) statement[i].reset(); }

  Stmt statements[LAST];
};
davka
  • 13,974
  • 11
  • 61
  • 86

5 Answers5

3

Use a statically sized array.

Stmt statements[10];

for(int i = 0; i < sizeof(statements) / sizeof(Stmt); i++)
    statements[i].reset();
Puppy
  • 144,682
  • 38
  • 256
  • 465
  • thanks, it occurred to me, but there is an issue - currently they each have a meaningful name and are used in various methods provided by the DbProxy class. With an array, I will have to refer to them by `statements[3]` rather than `insertStmt`, which may cause errors and confusion. – davka Jan 10 '11 at 16:32
  • I could perhaps use enum for this? please see the edit in my OP – davka Jan 10 '11 at 16:33
  • @davka: You could simply make little helper functions. `Stmt& GetInsertStmt() { return statements[3]; }`. An enum will also do the job. `enum { InsertStmt = 0, UpdateStmt = 1 };` where you can pass the enum directly as an array index. – Puppy Jan 10 '11 at 16:37
  • you could store an array of pointers to them instead; this would require you to setup the array pointers once though, eg in constructor: statements[0]=&a; statements[1]=&b; etc – stijn Jan 10 '11 at 16:38
  • @davka: Yes, that's exactly what I would do. – Puppy Jan 10 '11 at 17:10
  • thanks! (I have to make this comment longer so that SO would accept it... :) – davka Jan 10 '11 at 17:13
2

There's no need for the extra auto_ptr (each Stmt being an auto_ptr anyway), if you collect them in a single class you can reset them with a simple assignment. Unlike an array solution you still preserve their meaningful names.

Note that you can't use an unnamed temporary (e.g. stmts = OdbcResources();) as the generated copy assignment operator will take a non-const reference as the members (auto_ptr) cannot be assigned from non-const auto_ptrs.

class DbProxy {
public:
  DbProxy() : stmts() {}
private:
  void reset() { OdbcResources tmp; stmts = tmp; }

  struct OdbcResources {
    Stmt a;
    Stmt b;
    Stmt c;
    // ... about 10 more
  };
  OdbcResources stmts;
};
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • this is probably the simplest solution. Could you elaborate the second point, about the unnamed temporary? – davka Jan 11 '11 at 08:26
  • 2
    @davka: The compiler can't generate a copy assignment operator with the signature `OdbcResources& operator=( const OdbcResources& )` because the memberwise assignment will fail for `auto_ptr` which takes a non-`const` reference in its copy assignment operator. The compiler has to generate a copy assignment operator with the signature `OdbcResources& operator=( OdbcResources& )`. Because you can't bind a temporary to a non-`const` reference `stmts = OdbcResources()` now won't compile. – CB Bailey Jan 11 '11 at 08:34
  • BTW, if so, why am I able to compile a line like this: `aut_ptr ip = auto_ptr(new int);`? – davka Jan 11 '11 at 13:00
  • 1
    @davka: Because `auto_ptr` has some implicit conversions to and from `auto_ptr_ref` that are specifically there to allow his sort of thing (otherwise assigning or constructing a `std::auto_ptr` from the `std::auto_ptr` returned by value from a function couldn't work and that's a very important use case). If you use containing structure: `struct X { explicit X(int* q):p(q){} std::auto_ptr p; };` then you don't have any such implicit conversion helpers and you'll see that `X x = X(new int);` won't work. – CB Bailey Jan 11 '11 at 13:20
1

This is crying out for a container - assuming odbc::PreparedStatement is copyable, simply have a vector of these in DbProxy

class DbProxy {
private:
  void reset() { resources.clear(); } // all vanish!

  vector<odbc::PreparedStatement> resources;
};

Else, shared_ptr

typedef shared_ptr<odbc::PreparedStatement> Stmt;
class DbProxy {
private:
  void reset() { resources.clear(); } // all vanish!

  vector<Stmt> resources;
};
Nim
  • 33,299
  • 2
  • 62
  • 101
  • thanks. The library I use (odbc++) only gives me a pointer to the `odbc::PreparedStatement` so I can't use the first option. The second is interesting, though. – davka Jan 10 '11 at 16:50
  • If your library doesn't transfer ownership of the `odbc::PreparedStatement` to you, the second will also not work because both the `shared_ptr` and the library will think they have ownership. – Mark B Jan 10 '11 at 16:57
  • @Mark B: not sure - their samples use `auto_ptr` that deletes the pointer when goes out of scope, so I assume they do transfer the ownership (sort of, anyway, because when I reset the connection the statement belongs to, I get seg. fault when trying to use/delete the statement. So it looks like a shared ownership...) – davka Jan 10 '11 at 17:17
1

I see nothing wrong with this type of approach. This looks like the "Private Implementation" idiom. You can be interested in the details.

my2c

neuro
  • 14,948
  • 3
  • 36
  • 59
0

You might be able to simply copy in a default copy:

void reset() { *this = DbProxy(); }
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • this is interesting, but looks a bit brutal, isn't it? Anyway it assumes that I have nothing in the class **but** those statements. – davka Jan 10 '11 at 16:47
  • Yes, but what about `stmts.reset(new OdbcResources);` to just reset the prepared statements to default constructed items? – Mark B Jan 10 '11 at 17:00