Skip to content

Does not support classes with only explicit non-zero parameter constructors #15

@finger563

Description

@finger563

I have a class of the form:

template <typename T> class Bezier {
public:
  /**
   * @brief Unweighted cubic bezier configuration for 4 control points.
   */
  struct Config {
    std::array<T, 4> control_points; ///< Array of 4 control points
  };

  /**
   * @brief Weighted cubic bezier configuration for 4 control points with
   *        individual weights.
   */
  struct WeightedConfig {
    std::array<T, 4> control_points; ///< Array of 4 control points
    std::array<float, 4> weights = {1.0f, 1.0f, 1.0f,
                                    1.0f}; ///< Array of 4 weights, default is array of 1.0f
  };

  /**
   * @brief Construct an unweighted cubic bezier curve for evaluation.
   * @param config Unweighted Config structure containing the control points.
   */
  explicit Bezier(const Config &config)
      : weighted_(false)
      , control_points_(config.control_points) {}

  /**
   * @brief Construct a rational / weighted cubic bezier curve for evaluation.
   * @param config Rational / weighted WeightedConfig structure containing the
   *        control points and their weights.
   */
  explicit Bezier(const WeightedConfig &config)
      : weighted_(true)
      , control_points_(config.control_points)
      , weights_(config.weights) {}

Which (when configured with the options to specialize), generates the following binding code:

  ////////////////////    <generated_from:bezier.hpp>    ////////////////////
  auto pyClassBezier_espp_Vector2f =
      py::class_<espp::Bezier<espp::Vector2f>>
          (m, "Bezier_espp_Vector2f", py::dynamic_attr(), "*\n * @brief Implements rational / weighted and unweighted cubic bezier curves\n *        between control points.\n * @note See https://pomax.github.io/bezierinfo/ for information on bezier\n *       curves.\n * @note Template class which can be used individually on floating point\n *       values directly or on containers such as Vector2<float>.\n * @tparam T The type of the control points, e.g. float or Vector2<float>.\n * @note The bezier curve is defined by 4 control points, P0, P1, P2, P3.\n *      The curve is defined by the equation:\n *      \\f$B(t) = (1-t)^3 * P0 + 3 * (1-t)^2 * t * P1 + 3 * (1-t) * t^2 * P2 + t^3 * P3\\f$\n *      where t is the evaluation parameter, [0, 1].\n *\n * @note The weighted bezier curve is defined by 4 control points, P0, P1, P2, P3\n *      and 4 weights, W0, W1, W2, W3.\n *      The curve is defined by the equation:\n *      \\f$B(t) = (W0 * (1-t)^3 * P0 + W1 * 3 * (1-t)^2 * t * P1 + W2 * 3 * (1-t) * t^2 * P2 + W3 *\n * t^3 * P3) / (W0 + W1 + W2 + W3)\\f$ where t is the evaluation parameter, [0, 1].\n *\n * \\section bezier_ex1 Example\n * \\snippet math_example.cpp bezier example\n");

  { // inner classes & enums of Bezier_espp_Vector2f
      auto pyClassBezier_ClassConfig =
          py::class_<espp::Bezier::Config>
              (pyClassBezier, "Config", py::dynamic_attr(), "*\n   * @brief Unweighted cubic bezier configuration for 4 control points.\n")
          .def(py::init<>()) // implicit default constructor
          .def_readwrite("control_points", &espp::Bezier::Config::control_points, "/< Array of 4 control points")
          ;
      auto pyClassBezier_ClassWeightedConfig =
          py::class_<espp::Bezier::WeightedConfig>
              (pyClassBezier, "WeightedConfig", py::dynamic_attr(), "*\n   * @brief Weighted cubic bezier configuration for 4 control points with\n   *        individual weights.\n")
          .def(py::init<>()) // implicit default constructor
          .def_readwrite("control_points", &espp::Bezier::WeightedConfig::control_points, "/< Array of 4 control points")
          .def_readwrite("weights", &espp::Bezier::WeightedConfig::weights, "/< Array of 4 weights, default is array of 1.0")
          ;
  } // end of inner classes & enums of Bezier_espp_Vector2f

  pyClassBezier_espp_Vector2f
      .def(py::init<>()) // implicit default constructor
      .def("__call__",
          &espp::Bezier<espp::Vector2f>::operator(),
          py::arg("t"),
          "*\n   * @brief Evaluate the bezier at \\p t.\n   * @note Convienience wrapper around the at() method.\n   * @param t The evaluation parameter, [0, 1].\n   * @return The bezier evaluated at \\p t.\n")
      ;
  ////////////////////    </generated_from:bezier.hpp>    ////////////////////

There are two errors here:

  1. It incorrectly generates the implicit default constructor, and
  2. It does not properly generate the explicit constructors that are defined in the class.

This is not related to #14 (template specialization issue), as it also occurs on other classes I have which are not template classes but which also lack a default constructor and instead have explicit non-zero parameter constructors.

I've done some initial instrumentation in the python code to determine why this might be the case, and I'm seeing that it's properly setting flag_generate_void_ctor to false, and flag_generate_named_ctor_params to true, but for some reason (which I haven't figured out yet), it is unable to actually get the parameters for the constructor - instead returning a ctor_decl for which len(ctor_decl.parameter_list.parameters) == 0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions