diff --git a/dtool/src/dtoolbase/pvector.h b/dtool/src/dtoolbase/pvector.h index c9d89fe942..74f51d2d50 100644 --- a/dtool/src/dtoolbase/pvector.h +++ b/dtool/src/dtoolbase/pvector.h @@ -39,11 +39,25 @@ public: typedef vector 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 ©) : 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 &&from) NOEXCEPT : base_class(move(from)) {}; + + pvector &operator =(pvector &&from) NOEXCEPT { + base_class::operator =(move(from)); + return *this; + } +#endif + + pvector &operator =(const pvector ©) { + base_class::operator =(copy); + return *this; + } }; #endif // USE_STL_ALLOCATOR diff --git a/dtool/src/dtoolbase/typeRegistry.h b/dtool/src/dtoolbase/typeRegistry.h index 1526b6bc2f..6c668a6190 100644 --- a/dtool/src/dtoolbase/typeRegistry.h +++ b/dtool/src/dtoolbase/typeRegistry.h @@ -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; diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 4dae961c0d..04fc0e653a 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -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') diff --git a/panda/src/putil/bamReader.I b/panda/src/putil/bamReader.I index 19f1a37a9d..ece2676956 100644 --- a/panda/src/putil/bamReader.I +++ b/panda/src/putil/bamReader.I @@ -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 */ diff --git a/panda/src/putil/bamReader.h b/panda/src/putil/bamReader.h index 885fddaffb..4785a4d4fc 100644 --- a/panda/src/putil/bamReader.h +++ b/panda/src/putil/bamReader.h @@ -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(); diff --git a/panda/src/putil/bamReader_ext.cxx b/panda/src/putil/bamReader_ext.cxx new file mode 100644 index 0000000000..4a490f90d9 --- /dev/null +++ b/panda/src/putil/bamReader_ext.cxx @@ -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 ¶ms){ + 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:: +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:: +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 diff --git a/panda/src/putil/bamReader_ext.h b/panda/src/putil/bamReader_ext.h new file mode 100644 index 0000000000..d7905c617f --- /dev/null +++ b/panda/src/putil/bamReader_ext.h @@ -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 : public ExtensionBase { +public: + PyObject *get_file_version() const; + + static void register_factory(TypeHandle handle, PyObject *func); +}; + +#endif // HAVE_PYTHON + +#endif // BAMREADER_EXT_H diff --git a/panda/src/putil/factory.I b/panda/src/putil/factory.I index 37da03c686..57d3fadff5 100644 --- a/panda/src/putil/factory.I +++ b/panda/src/putil/factory.I @@ -70,6 +70,6 @@ make_instance_more_general(const string &type_name, */ template INLINE void Factory:: -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); } diff --git a/panda/src/putil/factory.h b/panda/src/putil/factory.h index f45f1ffce3..4520153911 100644 --- a/panda/src/putil/factory.h +++ b/panda/src/putil/factory.h @@ -49,7 +49,8 @@ public: make_instance_more_general(const string &type_name, const FactoryParams ¶ms = FactoryParams()); - INLINE void register_factory(TypeHandle handle, CreateFunc *func); + INLINE void register_factory(TypeHandle handle, CreateFunc *func, + void *user_data = NULL); }; #include "factory.I" diff --git a/panda/src/putil/factoryBase.cxx b/panda/src/putil/factoryBase.cxx index 935ad7bb33..52704e92cd 100644 --- a/panda/src/putil/factoryBase.cxx +++ b/panda/src/putil/factoryBase.cxx @@ -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 ¶ms) { +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 ¶ms) { * instance could not be created. */ TypedObject *FactoryBase:: -make_instance_more_specific(TypeHandle handle, const FactoryParams ¶ms) { +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 ¶ms) { 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; } diff --git a/panda/src/putil/factoryBase.h b/panda/src/putil/factoryBase.h index 13ea0c8eaa..6fcfc2f54f 100644 --- a/panda/src/putil/factoryBase.h +++ b/panda/src/putil/factoryBase.h @@ -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 ©); // internal utility functions - TypedObject *make_instance_exact(TypeHandle handle, - const FactoryParams ¶ms); + TypedObject *make_instance_exact(TypeHandle handle, FactoryParams params); TypedObject *make_instance_more_specific(TypeHandle handle, - const FactoryParams ¶ms); + 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 Creators; -#else - typedef pmap Creators; -#endif - + typedef pmap Creators; Creators _creators; typedef pvector Preferred; diff --git a/panda/src/putil/factoryParams.I b/panda/src/putil/factoryParams.I index 3183ae718e..0c6fdd09e7 100644 --- a/panda/src/putil/factoryParams.I +++ b/panda/src/putil/factoryParams.I @@ -13,6 +13,54 @@ #include "pnotify.h" +/** + * + */ +INLINE FactoryParams:: +FactoryParams() : _user_data(NULL) { +} + +/** + * + */ +INLINE FactoryParams:: +FactoryParams(const FactoryParams ©) : + _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 diff --git a/panda/src/putil/factoryParams.cxx b/panda/src/putil/factoryParams.cxx index 47e41ebcf3..6224f8b94e 100644 --- a/panda/src/putil/factoryParams.cxx +++ b/panda/src/putil/factoryParams.cxx @@ -13,20 +13,6 @@ #include "factoryParams.h" -/** - * - */ -FactoryParams:: -FactoryParams() { -} - -/** - * - */ -FactoryParams:: -~FactoryParams() { -} - /** * */ diff --git a/panda/src/putil/factoryParams.h b/panda/src/putil/factoryParams.h index 428399dd85..5c45a6e2ee 100644 --- a/panda/src/putil/factoryParams.h +++ b/panda/src/putil/factoryParams.h @@ -35,8 +35,14 @@ */ class EXPCL_PANDA_PUTIL FactoryParams { public: - FactoryParams(); - ~FactoryParams(); + INLINE FactoryParams(); + INLINE FactoryParams(const FactoryParams ©); + 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 diff --git a/panda/src/putil/p3putil_ext_composite.cxx b/panda/src/putil/p3putil_ext_composite.cxx new file mode 100644 index 0000000000..4bedeea559 --- /dev/null +++ b/panda/src/putil/p3putil_ext_composite.cxx @@ -0,0 +1,3 @@ +#include "bamReader_ext.cxx" +#include "pythonCallbackObject.cxx" +#include "typedWritable_ext.cxx" diff --git a/panda/src/putil/typedWritable.h b/panda/src/putil/typedWritable.h index 20d7a7cb67..8278efba95 100644 --- a/panda/src/putil/typedWritable.h +++ b/panda/src/putil/typedWritable.h @@ -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();