wlav / cppyy

Other
387 stars 39 forks source link

Public member functions from .ipp files get loaded into global namespace #170

Open sophiehourihane opened 1 year ago

sophiehourihane commented 1 year ago

I am not sure if this is intended behavior. I have a file interval.hpp and an associate .ipp file. I notice that when I run dir(cppyy.gbl) the public functions defined there are loaded into the global namespace.

So before including the header, dir(cppyy.gbl) returns this:

['CppyyLegacy', '__add__', '__bool__', '__class__', '__delattr__', '__destruct__', '__dict__', '__dir__', '__dispatch__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__invert__', '__le__', '__lt__', '__module__', '__mul__', '__ne__', '__neg__', '__new__', '__pos__', '__python_owns__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__reshape__', '__rmul__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__smartptr__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__weakref__', 'int8_t', 'std', 'uint8_t']

after including the header dir(cppyy.gbl) returns this:

 ['CppyyLegacy', '__add__', '__bool__', '__class__', '__delattr__', '__destruct__', '__dict__', '__dir__', '__dispatch__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__invert__', '__le__', '__lt__', '__module__', '__mul__', '__ne__', '__neg__', '__new__', '__pos__', '__python_owns__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__reshape__', '__rmul__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__smartptr__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__weakref__', 'buildExclusiveBound', 'buildInclusiveBound', 'buildInfiniteBound', 'getValue', 'hasValue', 'int8_t', 'isExclusive', 'isInclusive', 'isInfinite', 'std', 'uint8_t']

Is this expected behavior? I am worried because some different classes have the same name for different functions. If this is how they are loaded in, will there be conflicts?

If it is helpful this is the contents of bound.hpp

/**
 * @brief A Bound<TValueType> represents a finite or infinite bound on some quantity.
 *
 * @details A Bound<TValueType> represents a finite or infinite bound on some quantity.
 * An infinite bound has no value; a finite bound has a value, and might be inclusive or exclusive.
 * Bounds are useful for building ProbabilityDistributions.
 * @tparam TValueType the type of the quantity
 */
template <typename TValueType>
class Bound {
 public:
  /**
   * @brief Alternate name for the type of this bound's value
   * @note This alternate name is provided for readability; even after a class has been stamped out
   * using this class template, you can still refer to the target type as ClassName::ValueType
   * when declaring variables, etc. without using the actual type by name. Otherwise, the template
   * type parameter "TValueType" would no longer be accessible once the class template is stamped
   * out into a class.
   */
  using ValueType = TValueType;

  /**
   * @brief Create and return a representation of a bound that does not exist (for example, an
   * upper bound that does not exist means that a value can be arbitrarily large)
   *
   * @return Bound representing a bound that does not exist
   */
  static Bound<ValueType> buildInfiniteBound();
  /**
   * @brief Create and return a representation of an inclusive bound at the given value
   *
   * @param value the value at the inclusive bound
   *
   * @return Bound representing an inclusive bound at the given value
   */
  static Bound<ValueType> buildInclusiveBound(TValueType value);
  /**
   * @brief Create and return a representation of an exclusive bound at the given value
   *
   * @param value the value at the exclusive bound
   *
   * @return Bound representing an exclusive bound at the given value
   */
  static Bound<ValueType> buildExclusiveBound(TValueType value);

  /**
   * @brief Return a bool indicating whether the bound is infinite
   *
   * @return true if the bound is infinite and false otherwise
   */
  [[nodiscard]] bool isInfinite() const;

  /**
   * @brief Return a bool indicating whether the bound is inclusive
   *
   * @return true if the bound is inclusive and false otherwise
   */
  [[nodiscard]] bool isInclusive() const;

  /**
   * @brief Return a bool indicating whether the bound is inclusive
   *
   * @return true if the bound is inclusive and false otherwise
   */
  [[nodiscard]] bool isExclusive() const;

  /**
   * @brief Return a boolean indicating whether the bound has a value (is finite)
   *
   * @return true if the bound has a value and false otherwise
   */
  [[nodiscard]] bool hasValue() const;

  /**
   * @brief Return the Bound's value, assuming the Bound is finite
   *
   * @return the Bound's value
   * @throw std::logic_error if the Bound has no value (is infinite)
   */
  [[nodiscard]] TValueType getValue() const;

  /**
   * @brief Return a boolean indicating whether or this bound equals the other
   *
   * @param other const reference to the other bound to check for equality against this one
   * @return true if the bounds are equal and false otherwise
   */
  [[nodiscard]] bool operator==(const Bound<TValueType>& other) const;

  /**
   * @brief Return a boolean indicating whether or this bound does not equal the other
   *
   * @param other const reference to the other bound to check for equality against this one
   * @return false if the bounds are equal and true otherwise
   */
  [[nodiscard]] bool operator!=(const Bound<TValueType>& other) const;

 private:
  /** An enum containing the categories of Bounds */
  enum BoundCategory { Infinite, Inclusive, Exclusive };

  /**
   * @brief Build a bound with the given info
   * @param category the category of the bound
   * @param value The value at the Bound; if the value is empty, then the bound is infinite
   * (has no value)
   * @note This constructor is private so force users to use the build methods above, which have
   * descriptive names and which require less configuration
   */
  Bound(BoundCategory category, std::optional<TValueType> value);

  /** The category of the Bound */
  BoundCategory category_;

  /** The value at the Bound; if it is empty, then the bound is infinite (has no value) */
  std::optional<TValueType> value_;
};

#include "src/series/bound.ipp"

#endif /* SRC_SERIES_BOUND_HPP_ */

If it is helpful this is the contents of bound.ipp


#include <optional>
#include <stdexcept>

template <typename TValueType>
Bound<TValueType> Bound<TValueType>::buildInfiniteBound() {
  return Bound<TValueType>{BoundCategory::Infinite, std::nullopt};
}

template <typename TValueType>
Bound<TValueType> Bound<TValueType>::buildInclusiveBound(TValueType value) {
  return Bound<TValueType>{BoundCategory::Inclusive, value};
}

template <typename TValueType>
Bound<TValueType> Bound<TValueType>::buildExclusiveBound(TValueType value) {
  return Bound<TValueType>{BoundCategory::Exclusive, value};
}

template <typename TValueType>
Bound<TValueType>::Bound(BoundCategory category, std::optional<TValueType> value) : category_{category}, value_{value} {}

template <typename TValueType>
bool Bound<TValueType>::isInfinite() const {
  return category_ == BoundCategory::Infinite;
}

template <typename TValueType>
bool Bound<TValueType>::isInclusive() const {
  return category_ == BoundCategory::Inclusive;
}

template <typename TValueType>
bool Bound<TValueType>::isExclusive() const {
  return category_ == BoundCategory::Exclusive;
}

template <typename TValueType>
bool Bound<TValueType>::hasValue() const {
  return static_cast<bool>(value_);
}

template <typename TValueType>
TValueType Bound<TValueType>::getValue() const {
  if (!value_) {
    throw std::logic_error("Attempted to getValue() on a Bound<TValueType> with no value (an infinite bound); verify that a Bound<TValueType> has a value with hasValue() before calling getValue()");
  }

  return value_.value();
}

template <typename TValueType>
bool Bound<TValueType>::operator==(const Bound<TValueType>& other) const {
  return category_ == other.category_ && value_ == other.value_;
}

template <typename TValueType>
bool Bound<TValueType>::operator!=(const Bound<TValueType>& other) const {
  return !(*this == other);
}
wlav commented 1 year ago

I'll have a look, but note that dir() isn't actually loading anything, only displaying.