Are Ghost objects really useful for implementing things like triggers? They needs to be polled for changes and it will be a lot of overhead. Also I don't like them because it's a separate type of object, it's not static object, not kinematic, not dynamic, but "ghost" object, phew. So I cannot really have triggers for dynamic vs kinematic object collision, for example, if I use ghost objects (if I want kinematic object to move by animation when it gets touched by dynamic object). Overall I think ghost objects is a bad design idea - it should be part of collision object functionality that can be turned on, or added into it optionally (like setting callback function/object).
Answering original question:
For my project I tried various methods for implementing triggers and ended setting up gContactProcessedCallback and gContactDestroyedCallback handlers and btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK for all objects requiring trigger functionality. Inside callbacks I quickly check if I'm interested in this contact by checking flag in pointer to my collision object wrapper (stored for every bullet collision object in user pointer) and decide if I want to further process it:
( btManifoldPoint& cp
, void* body0
, void* body1 )
Physics::CollisionObject * ourCollObj0 = reinterpret_cast<Physics::CollisionObject*>
Physics::CollisionObject * ourCollObj1 = reinterpret_cast<Physics::CollisionObject*>
if (!ourCollObj0->trigger_ && !ourCollObj1->trigger_)
return false; // Not interested in this.
Physics::Engine* physEngine = ourCollObj0->engine_;
(cp, ourCollObj0, ourCollObj1);