Browse Source

Support defining bam factory functions in Python

rdb 9 years ago
parent
commit
33ed30b178

+ 17 - 3
dtool/src/dtoolbase/pvector.h

@@ -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

+ 3 - 2
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;
 

+ 2 - 4
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')

+ 11 - 0
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
  */

+ 9 - 0
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();
 

+ 123 - 0
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 &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

+ 39 - 0
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<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

+ 2 - 2
panda/src/putil/factory.I

@@ -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);
 }

+ 2 - 1
panda/src/putil/factory.h

@@ -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"

+ 18 - 10
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 &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;
       }

+ 8 - 12
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 &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;

+ 48 - 0
panda/src/putil/factoryParams.I

@@ -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

+ 0 - 14
panda/src/putil/factoryParams.cxx

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

+ 13 - 2
panda/src/putil/factoryParams.h

@@ -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>

+ 3 - 0
panda/src/putil/p3putil_ext_composite.cxx

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

+ 3 - 0
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();