O
Owen Jacobson
Salve. I'm running into a compilation problem trying to write some
code using libpqxx[0] ( <http://pqxx.tk/> )'s pqxx::Connection and
pqxx::Transactor classes. I seem to be running into a problem with
templates that I do not understand.
I have a class that provides a Transactor (a functor that operates on a
database, in pqxx) to check for sessions. The interface is as follows:
--SessionTransaction.hpp--
#ifndef SESSIONTRANSACTION
#define SESSIONTRANSACTION
#include <string>
#include <pqxx/transactor.h>
#include "Session.hpp"
namespace lawn {
// slight naming goof here, NMF --owen
// XXX Fix this class name?
class SessionTransaction: public pqxx::Transactor {
public:
SessionTransaction (unsigned long sessionid,
const std::string& host);
void operator() (argument_type &T);
/// Sends data to mtarget, if defined.
void OnCommit ();
/// PQXX passes the transactor in as const, so we need a way to
/// communicate data back to the originating Session.
void SetSession (Session* target);
private:
bool mlogin;
unsigned long msid;
std::string mhost;
Session* mtarget;
};
}
#endif
--EOF--
The implementation for this class has not yet been written. I am
including this header in another part of the project, which *has* been
(partially) written, and attempting to compile only that file while I
complete the implementation and work out how SessionTransaction needs
to behave.
--Session.hpp--
#ifndef SESSION
#define SESSION
#include <cgicc/Cgicc.h>
#include "Database.hpp"
namespace lawn {
class Session {
public:
Session (const cgicc::Cgicc& cgi, DB_Handle db);
void SetUserid (unsigned long userid); //< called by
SessionTransaction::OnCommit, mainly
private:
bool mloggedin;
unsigned long muserid;
};
};
#endif
--EOF--
--Session.cpp--
#include <sstream>
#include <vector>
#include "Session.hpp"
#include "SessionTransaction.hpp"
namespace lawn {
Session::Session (const cgicc::Cgicc& cgi, DB_Handle db):
mloggedin (false),
muserid (0)
{
cgicc::CgiEnvironment env = cgi.getEnvironment ();
bool foundid = false;
unsigned long sessionid = 0;
std::vector<cgicc::HTTPCookie> cookies = env.getCookieList ();
for (std::vector<cgicc::HTTPCookie>::iterator i = cookies.begin ();
i != cookies.end ();
i++) {
if (i->getName () == "lawn_session") {
foundid = true; // it's possible to find an id of 0
std::stringstream idstream (i->getValue ());
idstream >> sessionid;
}
}
if (foundid) {
SessionTransaction strans (sessionid, env.getRemoteAddr ());
strans.SetSession (this);
db->Perform (strans, 1); // This is line 28 in Session.cpp
}
};
}
--EOF--
All of which conforms to the way the examples in the libpqxx
distribution seem to be written. However, upon attempting to compile
Session.cpp into Session.o, the following errors occur:
[oj@acolyte src]$ make Session.o
g++ -g -O3 -Wall -pedantic -ansi -I/usr/local/pqxx/include -c -o
Session.o Session.cpp
/usr/local/pqxx/include/pqxx/connection.h: In member function `void
pqxx::Connection:erform(const TRANSACTOR&, int) [with TRANSACTOR =
lawn::SessionTransaction]':
Session.cpp:28: instantiated from here
/usr/local/pqxx/include/pqxx/connection.h:296: `X' has incomplete type
/usr/local/pqxx/include/pqxx/connection.h:296: storage size of `X'
isn't known
/usr/local/pqxx/include/pqxx/connection.h:297: no match for call to `(
lawn::SessionTransaction) (<typeprefixerror>&)'
SessionTransaction.hpp:14: candidates are: void
lawn::SessionTransaction:perator()(pqxx::Transaction&)
/usr/local/pqxx/include/pqxx/connection.h:301: invalid use of undefined
type `
const struct pqxx::in_doubt_error'
/usr/local/pqxx/include/pqxx/connection.h:40: forward declaration of
`const
struct pqxx::in_doubt_error'
/usr/local/pqxx/include/pqxx/connection.h:301: warning: `...' handler
must be
the last handler for its try block
make: *** [Session.o] Error 1
It looks to me like the compiler (G++ 3.2.2) believes that
SessionTransaction is an incomplete specialisation of something. For
hints, I had a look at the referenced section of connection.h:
--connection.h segment--
template<typename TRANSACTOR>
inline void Connection:erform(const TRANSACTOR &T,
int Attempts)
//[t4]
{
if (Attempts <= 0) return;
bool Done = false;
// Make attempts to perform T
// TODO: Differentiate between db-related exceptions and other
exceptions?
do
{
--Attempts;
// Work on a copy of T2 so we can restore the starting situation if
need be
TRANSACTOR T2(T);
try
{
typename TRANSACTOR::argument_type X(*this, T2.Name());
T2(X); // Line 296
X.Commit(); // Line 297
Done = true;
}
catch (const in_doubt_error &)
{
// Not sure whether transaction went through or not. The last
thing in
// the world that we should do now is retry.
T2.OnDoubt();
throw;
}
catch (const PGSTD::exception &e)
{
// Could be any kind of error.
T2.OnAbort(e.what());
if (Attempts <= 0) throw;
continue;
}
catch (...)
{
// Don't try to forge ahead if we don't even know what happened
T2.OnAbort("Unknown exception");
throw;
}
T2.OnCommit();
} while (!Done);
}
--End of segment--
Interestingly, other files that use connection.h but do not call
pqxx::Connection:erform (...) compile fine, as do the various
examples provided with the library.
Any thoughts, or should I take this to the libpqxx guys and leave this
july group alone?
--Owen
[0] I know where the libpqxx support group is as well and will be
posting there shortly; I suspect the problem is with my understanding
of templates, hence my post here.
code using libpqxx[0] ( <http://pqxx.tk/> )'s pqxx::Connection and
pqxx::Transactor classes. I seem to be running into a problem with
templates that I do not understand.
I have a class that provides a Transactor (a functor that operates on a
database, in pqxx) to check for sessions. The interface is as follows:
--SessionTransaction.hpp--
#ifndef SESSIONTRANSACTION
#define SESSIONTRANSACTION
#include <string>
#include <pqxx/transactor.h>
#include "Session.hpp"
namespace lawn {
// slight naming goof here, NMF --owen
// XXX Fix this class name?
class SessionTransaction: public pqxx::Transactor {
public:
SessionTransaction (unsigned long sessionid,
const std::string& host);
void operator() (argument_type &T);
/// Sends data to mtarget, if defined.
void OnCommit ();
/// PQXX passes the transactor in as const, so we need a way to
/// communicate data back to the originating Session.
void SetSession (Session* target);
private:
bool mlogin;
unsigned long msid;
std::string mhost;
Session* mtarget;
};
}
#endif
--EOF--
The implementation for this class has not yet been written. I am
including this header in another part of the project, which *has* been
(partially) written, and attempting to compile only that file while I
complete the implementation and work out how SessionTransaction needs
to behave.
--Session.hpp--
#ifndef SESSION
#define SESSION
#include <cgicc/Cgicc.h>
#include "Database.hpp"
namespace lawn {
class Session {
public:
Session (const cgicc::Cgicc& cgi, DB_Handle db);
void SetUserid (unsigned long userid); //< called by
SessionTransaction::OnCommit, mainly
private:
bool mloggedin;
unsigned long muserid;
};
};
#endif
--EOF--
--Session.cpp--
#include <sstream>
#include <vector>
#include "Session.hpp"
#include "SessionTransaction.hpp"
namespace lawn {
Session::Session (const cgicc::Cgicc& cgi, DB_Handle db):
mloggedin (false),
muserid (0)
{
cgicc::CgiEnvironment env = cgi.getEnvironment ();
bool foundid = false;
unsigned long sessionid = 0;
std::vector<cgicc::HTTPCookie> cookies = env.getCookieList ();
for (std::vector<cgicc::HTTPCookie>::iterator i = cookies.begin ();
i != cookies.end ();
i++) {
if (i->getName () == "lawn_session") {
foundid = true; // it's possible to find an id of 0
std::stringstream idstream (i->getValue ());
idstream >> sessionid;
}
}
if (foundid) {
SessionTransaction strans (sessionid, env.getRemoteAddr ());
strans.SetSession (this);
db->Perform (strans, 1); // This is line 28 in Session.cpp
}
};
}
--EOF--
All of which conforms to the way the examples in the libpqxx
distribution seem to be written. However, upon attempting to compile
Session.cpp into Session.o, the following errors occur:
[oj@acolyte src]$ make Session.o
g++ -g -O3 -Wall -pedantic -ansi -I/usr/local/pqxx/include -c -o
Session.o Session.cpp
/usr/local/pqxx/include/pqxx/connection.h: In member function `void
pqxx::Connection:erform(const TRANSACTOR&, int) [with TRANSACTOR =
lawn::SessionTransaction]':
Session.cpp:28: instantiated from here
/usr/local/pqxx/include/pqxx/connection.h:296: `X' has incomplete type
/usr/local/pqxx/include/pqxx/connection.h:296: storage size of `X'
isn't known
/usr/local/pqxx/include/pqxx/connection.h:297: no match for call to `(
lawn::SessionTransaction) (<typeprefixerror>&)'
SessionTransaction.hpp:14: candidates are: void
lawn::SessionTransaction:perator()(pqxx::Transaction&)
/usr/local/pqxx/include/pqxx/connection.h:301: invalid use of undefined
type `
const struct pqxx::in_doubt_error'
/usr/local/pqxx/include/pqxx/connection.h:40: forward declaration of
`const
struct pqxx::in_doubt_error'
/usr/local/pqxx/include/pqxx/connection.h:301: warning: `...' handler
must be
the last handler for its try block
make: *** [Session.o] Error 1
It looks to me like the compiler (G++ 3.2.2) believes that
SessionTransaction is an incomplete specialisation of something. For
hints, I had a look at the referenced section of connection.h:
--connection.h segment--
template<typename TRANSACTOR>
inline void Connection:erform(const TRANSACTOR &T,
int Attempts)
//[t4]
{
if (Attempts <= 0) return;
bool Done = false;
// Make attempts to perform T
// TODO: Differentiate between db-related exceptions and other
exceptions?
do
{
--Attempts;
// Work on a copy of T2 so we can restore the starting situation if
need be
TRANSACTOR T2(T);
try
{
typename TRANSACTOR::argument_type X(*this, T2.Name());
T2(X); // Line 296
X.Commit(); // Line 297
Done = true;
}
catch (const in_doubt_error &)
{
// Not sure whether transaction went through or not. The last
thing in
// the world that we should do now is retry.
T2.OnDoubt();
throw;
}
catch (const PGSTD::exception &e)
{
// Could be any kind of error.
T2.OnAbort(e.what());
if (Attempts <= 0) throw;
continue;
}
catch (...)
{
// Don't try to forge ahead if we don't even know what happened
T2.OnAbort("Unknown exception");
throw;
}
T2.OnCommit();
} while (!Done);
}
--End of segment--
Interestingly, other files that use connection.h but do not call
pqxx::Connection:erform (...) compile fine, as do the various
examples provided with the library.
Any thoughts, or should I take this to the libpqxx guys and leave this
july group alone?
--Owen
[0] I know where the libpqxx support group is as well and will be
posting there shortly; I suspect the problem is with my understanding
of templates, hence my post here.