Support defining bam factory functions in Python

This commit is contained in:
rdb 2016-04-09 19:24:19 +02:00
parent 12af395012
commit 33ed30b178
16 changed files with 301 additions and 50 deletions

View File

@ -39,11 +39,25 @@ public:
typedef vector<Type, allocator> base_class;
typedef TYPENAME base_class::size_type size_type;
pvector(TypeHandle type_handle = pvector_type_handle) : base_class(allocator(type_handle)) { }
explicit pvector(TypeHandle type_handle = pvector_type_handle) : base_class(allocator(type_handle)) { }
pvector(const pvector<Type> &copy) : base_class(copy) { }
pvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator(type_handle)) { }
pvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator(type_handle)) { }
explicit pvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator(type_handle)) { }
explicit pvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator(type_handle)) { }
pvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator(type_handle)) { }
#ifdef USE_MOVE_SEMANTICS
pvector(pvector<Type> &&from) NOEXCEPT : base_class(move(from)) {};
pvector<Type> &operator =(pvector<Type> &&from) NOEXCEPT {
base_class::operator =(move(from));
return *this;
}
#endif
pvector<Type> &operator =(const pvector<Type> &copy) {
base_class::operator =(copy);
return *this;
}
};
#endif // USE_STL_ALLOCATOR

View File

@ -37,14 +37,15 @@ class EXPCL_DTOOL TypeRegistry : public MemoryBase {
public:
// User code shouldn't generally need to call TypeRegistry::register_type()
// or record_derivation() directly; instead, use the register_type
// convenience function, defined below.
// convenience function, defined in register_type.h.
bool register_type(TypeHandle &type_handle, const string &name);
PUBLISHED:
TypeHandle register_dynamic_type(const string &name);
void record_derivation(TypeHandle child, TypeHandle parent);
void record_alternate_name(TypeHandle type, const string &name);
PUBLISHED:
TypeHandle find_type(const string &name) const;
TypeHandle find_type_by_id(int id) const;

View File

@ -3407,8 +3407,7 @@ if (not RUNTIME):
TargetAdd('libp3putil.in', opts=OPTS, input=IGATEFILES)
TargetAdd('libp3putil.in', opts=['IMOD:panda3d.core', 'ILIB:libp3putil', 'SRCDIR:panda/src/putil'])
TargetAdd('libp3putil_igate.obj', input='libp3putil.in', opts=["DEPENDENCYONLY"])
TargetAdd('p3putil_typedWritable_ext.obj', opts=OPTS, input='typedWritable_ext.cxx')
TargetAdd('p3putil_pythonCallbackObject.obj', opts=OPTS, input='pythonCallbackObject.cxx')
TargetAdd('p3putil_ext_composite.obj', opts=OPTS, input='p3putil_ext_composite.cxx')
#
# DIRECTORY: panda/src/audio/
@ -4009,8 +4008,7 @@ if (not RUNTIME):
if PkgSkip("FREETYPE")==0:
TargetAdd('core.pyd', input="libp3pnmtext_igate.obj")
TargetAdd('core.pyd', input='p3putil_typedWritable_ext.obj')
TargetAdd('core.pyd', input='p3putil_pythonCallbackObject.obj')
TargetAdd('core.pyd', input='p3putil_ext_composite.obj')
TargetAdd('core.pyd', input='p3pnmimage_pfmFile_ext.obj')
TargetAdd('core.pyd', input='p3event_pythonTask.obj')
TargetAdd('core.pyd', input='p3gobj_ext_composite.obj')

View File

@ -159,6 +159,17 @@ get_file_pos() {
return _source->get_file_pos();
}
/**
* Registers a factory function that is called when an object of the given
* type is encountered within the .bam stream.
*
* @param user_data an optional pointer to be passed along to the function.
*/
void BamReader::
register_factory(TypeHandle handle, WritableFactory::CreateFunc *func, void *user_data) {
get_factory()->register_factory(handle, func, user_data);
}
/**
* Returns the global WritableFactory for generating TypedWritable objects
*/

View File

@ -148,11 +148,14 @@ PUBLISHED:
INLINE int get_current_major_ver() const;
INLINE int get_current_minor_ver() const;
EXTENSION(PyObject *get_file_version() const);
PUBLISHED:
MAKE_PROPERTY(source, get_source, set_source);
MAKE_PROPERTY(filename, get_filename);
MAKE_PROPERTY(loader_options, get_loader_options, set_loader_options);
MAKE_PROPERTY(file_version, get_file_version);
MAKE_PROPERTY(file_endian, get_file_endian);
MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
@ -194,7 +197,13 @@ public:
INLINE streampos get_file_pos();
public:
INLINE static void register_factory(TypeHandle type, WritableFactory::CreateFunc *func,
void *user_data = NULL);
INLINE static WritableFactory *get_factory();
PUBLISHED:
EXTENSION(static void register_factory(TypeHandle handle, PyObject *func));
private:
INLINE static void create_factory();

View File

@ -0,0 +1,123 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file bamReader_ext.cxx
* @author rdb
* @date 2016-04-09
*/
#include "bamReader_ext.h"
#include "config_util.h"
#ifdef HAVE_PYTHON
#ifndef CPPPARSER
extern Dtool_PyTypedObject Dtool_BamReader;
extern Dtool_PyTypedObject Dtool_DatagramIterator;
extern Dtool_PyTypedObject Dtool_TypedWritable;
#endif // CPPPARSER
/**
* Factory function that calls the registered Python function.
*/
static TypedWritable *factory_callback(const FactoryParams &params){
PyObject *func = (PyObject *)params.get_user_data();
nassertr(func != NULL, NULL);
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
// Use PyGILState to protect this asynchronous call.
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
#endif
// Extract the parameters we will pass to the Python function.
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
PyObject *py_scan = DTool_CreatePyInstance(&scan, Dtool_DatagramIterator, false, false);
PyObject *py_manager = DTool_CreatePyInstance(manager, Dtool_BamReader, false, false);
PyObject *args = PyTuple_Pack(2, py_scan, py_manager);
// Now call the Python function.
Thread *current_thread = Thread::get_current_thread();
PyObject *result = current_thread->call_python_func(func, args);
Py_DECREF(args);
Py_DECREF(py_scan);
Py_DECREF(py_manager);
if (result == (PyObject *)NULL) {
util_cat.error()
<< "Exception occurred in Python factory function\n";
} else if (result == Py_None) {
util_cat.error()
<< "Python factory function returned None\n";
Py_DECREF(result);
result = NULL;
}
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
PyGILState_Release(gstate);
#endif
// Unwrap the returned TypedWritable object.
if (result == (PyObject *)NULL) {
return (TypedWritable *)NULL;
} else {
void *object = NULL;
Dtool_Call_ExtractThisPointer(result, Dtool_TypedWritable, &object);
TypedWritable *ptr = (TypedWritable *)object;
ReferenceCount *ref_count = ptr->as_reference_count();
if (ref_count != NULL) {
// If the Python pointer is the last reference to it, make sure that the
// object isn't destroyed. We do this by calling unref(), which
// decreases the reference count without destroying the object.
if (result->ob_refcnt <= 1) {
ref_count->unref();
// Tell the Python wrapper object that it shouldn't try to delete the
// object when it is destroyed.
((Dtool_PyInstDef *)result)->_memory_rules = false;
}
Py_DECREF(result);
}
return (TypedWritable *)object;
}
}
/**
* Returns the version number of the Bam file currently being read.
*/
PyObject *Extension<BamReader>::
get_file_version() const {
return Py_BuildValue("(ii)", _this->get_file_major_ver(),
_this->get_file_minor_ver());
}
/**
* Registers a Python function as factory function for the given type. This
* should be a function (or class object) that takes a DatagramIterator and a
* BamReader as arguments, and should return a TypedWritable object.
*/
void Extension<BamReader>::
register_factory(TypeHandle handle, PyObject *func) {
nassertv(func != NULL);
if (!PyCallable_Check(func)) {
Dtool_Raise_TypeError("second argument to register_factory must be callable");
return;
}
Py_INCREF(func);
BamReader::get_factory()->register_factory(handle, &factory_callback, (void *)func);
}
#endif

View File

@ -0,0 +1,39 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file bamReader_ext.h
* @author rdb
* @date 2016-04-09
*/
#ifndef BAMREADER_EXT_H
#define BAMREADER_EXT_H
#include "dtoolbase.h"
#ifdef HAVE_PYTHON
#include "extension.h"
#include "bamReader.h"
#include "py_panda.h"
/**
* This class defines the extension methods for BamReader, which are called
* instead of any C++ methods with the same prototype.
*/
template<>
class Extension<BamReader> : public ExtensionBase<BamReader> {
public:
PyObject *get_file_version() const;
static void register_factory(TypeHandle handle, PyObject *func);
};
#endif // HAVE_PYTHON
#endif // BAMREADER_EXT_H

View File

@ -70,6 +70,6 @@ make_instance_more_general(const string &type_name,
*/
template<class Type>
INLINE void Factory<Type>::
register_factory(TypeHandle handle, CreateFunc *func) {
FactoryBase::register_factory(handle, (BaseCreateFunc *)func);
register_factory(TypeHandle handle, CreateFunc *func, void *user_data) {
FactoryBase::register_factory(handle, (BaseCreateFunc *)func, user_data);
}

View File

@ -49,7 +49,8 @@ public:
make_instance_more_general(const string &type_name,
const FactoryParams &params = FactoryParams());
INLINE void register_factory(TypeHandle handle, CreateFunc *func);
INLINE void register_factory(TypeHandle handle, CreateFunc *func,
void *user_data = NULL);
};
#include "factory.I"

View File

@ -128,12 +128,18 @@ find_registered_type(TypeHandle handle) {
/**
* Registers a new kind of thing the Factory will be able to create.
*
* @param user_data an optional pointer to be passed along to the function.
*/
void FactoryBase::
register_factory(TypeHandle handle, BaseCreateFunc *func) {
register_factory(TypeHandle handle, BaseCreateFunc *func, void *user_data) {
nassertv(handle != TypeHandle::none());
nassertv(func != (BaseCreateFunc *)NULL);
_creators[handle] = func;
Creator creator;
creator._func = func;
creator._user_data = user_data;
_creators[handle] = creator;
}
/**
@ -234,15 +240,16 @@ operator = (const FactoryBase &) {
* not be created.
*/
TypedObject *FactoryBase::
make_instance_exact(TypeHandle handle, const FactoryParams &params) {
make_instance_exact(TypeHandle handle, FactoryParams params) {
Creators::const_iterator ci = _creators.find(handle);
if (ci == _creators.end()) {
return NULL;
}
BaseCreateFunc *func = (BaseCreateFunc *)((*ci).second);
nassertr(func != (BaseCreateFunc *)NULL, NULL);
return (*func)(params);
Creator creator = (*ci).second;
nassertr(creator._func != (BaseCreateFunc *)NULL, NULL);
params._user_data = creator._user_data;
return (*creator._func)(params);
}
/**
@ -251,7 +258,7 @@ make_instance_exact(TypeHandle handle, const FactoryParams &params) {
* instance could not be created.
*/
TypedObject *FactoryBase::
make_instance_more_specific(TypeHandle handle, const FactoryParams &params) {
make_instance_more_specific(TypeHandle handle, FactoryParams params) {
// First, walk through the established preferred list. Maybe one of these
// qualifies.
@ -272,9 +279,10 @@ make_instance_more_specific(TypeHandle handle, const FactoryParams &params) {
for (ci = _creators.begin(); ci != _creators.end(); ++ci) {
TypeHandle ctype = (*ci).first;
if (ctype.is_derived_from(handle)) {
BaseCreateFunc *func = (BaseCreateFunc *)((*ci).second);
nassertr(func != (BaseCreateFunc *)NULL, NULL);
TypedObject *object = (*func)(params);
Creator creator = (*ci).second;
nassertr(creator._func != (BaseCreateFunc *)NULL, NULL);
params._user_data = creator._user_data;
TypedObject *object = (*creator._func)(params);
if (object != (TypedObject *)NULL) {
return object;
}

View File

@ -56,7 +56,7 @@ public:
TypeHandle find_registered_type(TypeHandle handle);
void register_factory(TypeHandle handle, BaseCreateFunc *func);
void register_factory(TypeHandle handle, BaseCreateFunc *func, void *user_data = NULL);
int get_num_types() const;
TypeHandle get_type(int n) const;
@ -74,22 +74,18 @@ private:
void operator = (const FactoryBase &copy);
// internal utility functions
TypedObject *make_instance_exact(TypeHandle handle,
const FactoryParams &params);
TypedObject *make_instance_exact(TypeHandle handle, FactoryParams params);
TypedObject *make_instance_more_specific(TypeHandle handle,
const FactoryParams &params);
FactoryParams params);
private:
// internal mechanics and bookkeeping
struct Creator {
BaseCreateFunc *_func;
void *_user_data;
};
#if (defined(WIN32_VC) || defined(WIN64_VC)) && !defined(__ICL) //__ICL is Intel C++
// Visual C++ seems to have a problem with building a map based on
// BaseCreateFunc. We'll have to typecast it on the way out.
typedef pmap<TypeHandle, void *> Creators;
#else
typedef pmap<TypeHandle, BaseCreateFunc *> Creators;
#endif
typedef pmap<TypeHandle, Creator> Creators;
Creators _creators;
typedef pvector<TypeHandle> Preferred;

View File

@ -13,6 +13,54 @@
#include "pnotify.h"
/**
*
*/
INLINE FactoryParams::
FactoryParams() : _user_data(NULL) {
}
/**
*
*/
INLINE FactoryParams::
FactoryParams(const FactoryParams &copy) :
_params(copy._params),
_user_data(copy._user_data) {}
/**
*
*/
INLINE FactoryParams::
~FactoryParams() {
}
#ifdef USE_MOVE_SEMANTICS
/**
*
*/
INLINE FactoryParams::
FactoryParams(FactoryParams &&from) NOEXCEPT :
_params(move(from._params)),
_user_data(from._user_data) {}
/**
*
*/
INLINE void FactoryParams::
operator = (FactoryParams &&from) NOEXCEPT {
_params = move(from._params);
_user_data = from._user_data;
}
#endif
/**
* Returns the custom pointer that was associated with the factory function.
*/
INLINE void *FactoryParams::
get_user_data() const {
return _user_data;
}
/**
* A handy convenience template function that extracts a parameter of the

View File

@ -13,20 +13,6 @@
#include "factoryParams.h"
/**
*
*/
FactoryParams::
FactoryParams() {
}
/**
*
*/
FactoryParams::
~FactoryParams() {
}
/**
*
*/

View File

@ -35,8 +35,14 @@
*/
class EXPCL_PANDA_PUTIL FactoryParams {
public:
FactoryParams();
~FactoryParams();
INLINE FactoryParams();
INLINE FactoryParams(const FactoryParams &copy);
INLINE ~FactoryParams();
#ifdef USE_MOVE_SEMANTICS
INLINE FactoryParams(FactoryParams &&from) NOEXCEPT;
INLINE void operator = (FactoryParams &&from) NOEXCEPT;
#endif
void add_param(FactoryParam *param);
void clear();
@ -46,10 +52,15 @@ public:
FactoryParam *get_param_of_type(TypeHandle type) const;
INLINE void *get_user_data() const;
private:
typedef pvector< PT(TypedReferenceCount) > Params;
Params _params;
void *_user_data;
friend class FactoryBase;
};
template<class ParamType>

View File

@ -0,0 +1,3 @@
#include "bamReader_ext.cxx"
#include "pythonCallbackObject.cxx"
#include "typedWritable_ext.cxx"

View File

@ -47,7 +47,10 @@ public:
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager);
virtual bool require_fully_complete() const;
PUBLISHED:
virtual void fillin(DatagramIterator &scan, BamReader *manager);
public:
virtual void finalize(BamReader *manager);
virtual ReferenceCount *as_reference_count();