Namespace

IBPP introduces a C++ name space : 'IBPP'. Everything is defined inside that namespace. Depending on your application needs, on your mood or if name clashing happens, you could do a : <pre>using namespace IBPP;</pre> in your program files, or choose to use the (short) prefix IBPP:: at a lot of place. The choice is yours. In these notes however, we will assume the second option : prefixing everywhere needed with 'IBPP::'.

Exceptions

IBPP methods never return a status or error code. In each and every error situation, IBPP uses C++ exceptions to report unexpected things. Every IBPP interface can throw a class IBPP::Exception. That exception should always be caught by reference and you can use its methods to get more information on what triggered the exception :

try { ... }
catch (IBPP::Exception& e) { cout << e.ErrorMessage(); }

Here is an example of a typical output :

*** IBPP::Exception inside Statement::ExecuteImmediate ***
IBPP Message : isc_dsql_execute_immediate failed.
SQL Message : -104
Invalid token.
Engine Message :
Dynamic SQL Error
SQL error code = -104
Token unknown - line 1, char 7
SYNTAX
  • IBPP Message gives you an IBPP definition of the problem. When it is something specific to IBPP (as an assert failing), the message is most explicit. When it is a common dynamic SQL problem, this string indicates what Interbase C-API call failed.
  • SQL Message gives you the SQL standardized code and error string. This shows a -999 error code when the exception is not related to an SQL problem.
  • Engine Message is the more detailed error information retrieved from the Interbase engine itself.

Exception Hierarchy

IBPP::Exception is actually the base class of a (small) hierarchy of exception classes.

                    std::exception
                           |
                   IBPP::Exception
                 /                 \
    IBPP::LogicException    IBPP::SQLException
             |
      IBPP::WrongType

Initialization

There is no explicit IBPP library initialization. There is no dedicated call which you should do first in an application program to get IBPP initialized. There is an initialization step, though. It is automatically done when, and so defered until, you attempt something with its interfaces. On Win32 platform, this also means that the InterBase gds32.dll will be dynamically loaded at first use time. It may be usefull in some kind of tools where, if gds32.dll is not present on the system, the tool should be able to report the problem and not simply fail at startup with a cryptic windows error about dlls not found. The linux builds still use an early-bind to libgds. Late-binding will eventually be added later.

Versioning

Though there is no requirement to call an initialization routine, there is a highly recommended check to do in your app before use the library interfaces. The check consists in verifying that the ibpp.h header file which you used while compiling your own application code is indeed the right one or a compatible one with the exact version of the library you linked with. IBPP provides a C API to do that check : <pre>bool CheckVersion(unsigned long); … if (! CheckVersion(IBPP::Version)) …</pre>

*If everything goes well, CheckVersion() will return true. *If the library's version is not compatible with the ibpp.h you used, it will return false. It is up to you to not try using the interfaces. *If something more serious happened, it will throw an IBPP::Exception.

De-Initialization

There is no specific call to de-initialize (or free) the library after using it. When your program will exit, a global object internal to the ibpp library will have its destructor called (after your main() function returns) and that one will take care of whatever is needed.

Objects Instantiation and Disposal

IBPP objects are indeed kind of smart-pointers to object interfaces. Those interfaces are obtained through factory methods (which return a pointer to the requested interface) and you assign those pointers to the specific smart-pointers objects of IBPP. See the following example :

IBPP::Database db;
db = IBPP::DatabaseFactory("someserver", "somedatabase");

You can of course write it a little bit shorter and give yourself some freedom to assign such objects between them or re-assign a new (compatible) interface to one of the objects later :

IBPP::Database db = IBPP::DatabaseFactory("someserver", "somedatabase");
IBPP::Database db2 = db;
db = IBPP::DatabaseFactory("otherserver", "otherdb");

Those smart-pointers objects, if used as described, don't require any other cleanup mechanism than C++ provides you. They will get properly released and deleted when they will go out of scope, which, unless your compiler is buggy, guarantees they will also be reclaimed in case of exceptions.

IBPP Smart Pointers Reference

Class definition

This sample is taken from file ibpp.h as of version 2.5.0, please see the actual header file for up-to-date class definition.

template <class T>
class Ptr
{
private:
	T* mObject;

public:
	void clear()		{ if (mObject != 0) mObject->Release(mObject); }
	T* intf() const		{ return mObject; }
	T* operator->() const	{ return mObject; }
	Ptr& operator=(T* p)
	{
		// AddRef _before_ Release gives correct behaviour on self-assigns
		T* tmp = (p == 0 ? 0 : p->AddRef());	// Take care of 0
		if (mObject != 0) mObject->Release(mObject);
		mObject = tmp; return *this;
	}
	Ptr& operator=(const Ptr& r)
	{
		// AddRef _before_ Release gives correct behaviour on self-assigns
		T* tmp = (r.intf() == 0 ? 0 : r->AddRef());// Take care of 0
		if (mObject != 0) mObject->Release(mObject);
		mObject = tmp; return *this;
	}
	Ptr(T* p)		{ mObject = (p == 0 ? 0 : p->AddRef()); }
	Ptr(const Ptr& r)	{ mObject = (r.intf() == 0 ? 0 : r->AddRef()); }

	Ptr() : mObject(0) { }
	~Ptr() { clear(); }
};

Operator ->

Being smart-pointers, you use them as pointers.
That is you use the de-reference operator → to access their methods. Think that through these objects and the → operator, you actually call the methods of the attached interface and not of the smart-pointer itself. For people coming from IBPP 1.x this is also an easy migration path. You just have to modify your object declarations (removing the *) and suppress all your calls to the Release() methods which aren't required anymore in IBPP 2.x.

Method clear()

The IBPP objects also have a 'clear()' method, which allows you to free the smart-pointer from its attached interface (thereby releasing it) without waiting for the smart-pointer variable to go out of scope. This might be usefull in some specific case (for instance if you manage arrays or std::vectors of those objects). It is implied when you assign a new interface to an IBPP object : the previous interface gets released if required. Finally, IBPP implements a full reference-counting scheme so that if you can have for instance two IBPP::Database objects to the same interface instance. The interface instance will get released when the last smart-pointer referencing it will go out of scope, will get clear(), or will be assigned another interface. All in all, there is nothing which you should worry about these objects. Just use them.

Method intf()

Method intf() gives you access to the underlying interface pointer. It is mostly usefull in checks like the following to verify if this object is already bound or not to an interface. By definition, an unbound pointer returns 0 for its intf().

...
if (Db.intf() == 0) { ... }
...

Reference counting

Each interface class implements two methods used by the above smart pointer mechanism.

AddRef()

Increments the count of references to an interface instance. You should never have to use this method yourself.

Release()

Decrements the count of references to an interface instance. You should never have to use this method yourself.

reference/guidelines.txt · Last modified: 2006/03/30 18:09 (external edit)
 
Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki