Skip to content

Commit

Permalink
Merge pull request #239 from ruby-rice/dev
Browse files Browse the repository at this point in the history
Improved Callback Support
  • Loading branch information
cfis authored Jan 24, 2025
2 parents d5c04d8 + aa0b3e7 commit c2fb0de
Show file tree
Hide file tree
Showing 28 changed files with 623 additions and 187 deletions.
23 changes: 17 additions & 6 deletions rice/Arg.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ namespace Rice
*/
Arg(std::string name);

// Make Arg polymorphic so dynamic_cast works
virtual ~Arg() = default;

//! Set the default value for this Arg
/*! Set the default value for this argument.
* If this isn't called on this Arg, then this
Expand All @@ -56,31 +59,39 @@ namespace Rice

//! Tell the receiving object to keep this argument alive
//! until the receiving object is freed.
Arg& keepAlive();
virtual Arg& keepAlive();

//! Returns if the argument should be kept alive
bool isKeepAlive() const;

//! Specifies if the argument should be treated as a value
Arg& setValue();
virtual Arg& setValue();

//! Returns if the argument should be treated as a value
bool isValue() const;

//! Specifies if the argument is opaque and Rice should not convert it from Ruby to C++ or vice versa.
//! This is useful for callbacks and user provided data paramameters.
virtual Arg& setOpaque();

//! Returns if the argument should be treated as a value
bool isOpaque() const;

//! Specifies C++ will take ownership of this value and Ruby should not free it
Arg& transferOwnership();
bool isTransfer();
virtual Arg& takeOwnership();
bool isOwner();

public:
const std::string name;
std::string name;
int32_t position = -1;

private:
//! Our saved default value
std::any defaultValue_;
bool isValue_ = false;
bool isKeepAlive_ = false;
bool isTransfer_ = false;
bool isOwner_ = false;
bool isOpaque_ = false;
};
} // Rice

Expand Down
19 changes: 15 additions & 4 deletions rice/Arg.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,26 @@ namespace Rice
return isValue_;
}

inline Arg& Arg::transferOwnership()
inline Arg& Arg::setOpaque()
{
this->isTransfer_ = true;
isOpaque_ = true;
return *this;
}

inline bool Arg::isTransfer()
inline bool Arg::isOpaque() const
{
return this->isTransfer_;
return isOpaque_;
}

inline Arg& Arg::takeOwnership()
{
this->isOwner_ = true;
return *this;
}

inline bool Arg::isOwner()
{
return this->isOwner_;
}


Expand Down
21 changes: 21 additions & 0 deletions rice/Callback.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef Rice__Callback__hpp_
#define Rice__Callback__hpp_

namespace Rice
{
//! Define a callback.
/*! When C++ invokes a C style callback, Rice automatically converts the C++ arguments
* to Ruby. However, there may be cases where you need to specify how individual arguments
* should be handled. For example, callbacks often have a user data parameter which is
* defined as a void pointer (void*). In this case, you need to tell Ruby that the parameter
* is opaque and should not be convered. For example:
*
* define_callback<void(*)(void*)>(Arg("user_data").setOpaque());
*
* \param args a list of Arg instance used to define default parameters (optional)
* \return nothing
*/
template<typename Callback_T, typename...Arg_Ts>
void define_callback(const Arg_Ts&...args);
}
#endif // Rice__Callback__hpp_
13 changes: 13 additions & 0 deletions rice/Callback.ipp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Rice
{
template<typename Callback_T, typename...Arg_Ts>
void define_callback(const Arg_Ts&...args)
{
MethodInfo* methodInfo = new MethodInfo(detail::function_traits<Callback_T>::arity, args...);
#ifdef HAVE_LIBFFI
detail::NativeCallbackFFI<Callback_T>::setMethodInfo(methodInfo);
#else
detail::NativeCallbackSimple<Callback_T>::setMethodInfo(methodInfo);
#endif
}
}
2 changes: 1 addition & 1 deletion rice/Data_Object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ namespace Rice
static_assert(!std::is_void_v<T>);

public:
static T* from_ruby(VALUE value, bool transferOwnership = false);
static T* from_ruby(VALUE value, bool takeOwnership = false);

public:
//! Wrap a C++ object.
Expand Down
49 changes: 7 additions & 42 deletions rice/Data_Object.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ namespace Rice
}

template<typename T>
inline T* Data_Object<T>::from_ruby(VALUE value, bool transferOwnership)
inline T* Data_Object<T>::from_ruby(VALUE value, bool takeOwnership)
{
if (Data_Type<T>::is_descendant(value))
{
return detail::unwrap<T>(value, Data_Type<T>::ruby_data_type(), transferOwnership);
return detail::unwrap<T>(value, Data_Type<T>::ruby_data_type(), takeOwnership);
}
else
{
Expand Down Expand Up @@ -193,41 +193,6 @@ namespace Rice::detail
Return* returnInfo_ = nullptr;
};

template <>
class To_Ruby<void*>
{
public:
To_Ruby() = default;

explicit To_Ruby(Return* returnInfo) : returnInfo_(returnInfo)
{
}

VALUE convert(void* data)
{
if (data)
{
// Note that T could be a pointer or reference to a base class while data is in fact a
// child class. Lookup the correct type so we return an instance of the correct Ruby class
std::pair<VALUE, rb_data_type_t*> rubyTypeInfo = detail::Registries::instance.types.figureType(data);
bool isOwner = this->returnInfo_ && this->returnInfo_->isOwner();
return detail::wrap(rubyTypeInfo.first, rubyTypeInfo.second, data, isOwner);
}
else
{
return Qnil;
}
}

VALUE convert(const void* data)
{
return convert((void*)data);
}

private:
Return* returnInfo_ = nullptr;
};

template <typename T>
class To_Ruby<T*&>
{
Expand Down Expand Up @@ -364,7 +329,7 @@ namespace Rice::detail
}
else
{
return *Data_Object<Intrinsic_T>::from_ruby(value, this->arg_ && this->arg_->isTransfer());
return *Data_Object<Intrinsic_T>::from_ruby(value, this->arg_ && this->arg_->isOwner());
}
}

Expand Down Expand Up @@ -406,7 +371,7 @@ namespace Rice::detail
}
else
{
return *Data_Object<Intrinsic_T>::from_ruby(value, this->arg_ && this->arg_->isTransfer());
return *Data_Object<Intrinsic_T>::from_ruby(value, this->arg_ && this->arg_->isOwner());
}
}

Expand Down Expand Up @@ -448,7 +413,7 @@ namespace Rice::detail
}
else
{
return std::move(*Data_Object<Intrinsic_T>::from_ruby(value, this->arg_ && this->arg_->isTransfer()));
return std::move(*Data_Object<Intrinsic_T>::from_ruby(value, this->arg_ && this->arg_->isOwner()));
}
}

Expand Down Expand Up @@ -493,7 +458,7 @@ namespace Rice::detail
}
else
{
return Data_Object<Intrinsic_T>::from_ruby(value, this->arg_ && this->arg_->isTransfer());
return Data_Object<Intrinsic_T>::from_ruby(value, this->arg_ && this->arg_->isOwner());
}
}

Expand Down Expand Up @@ -535,7 +500,7 @@ namespace Rice::detail
}
else
{
return Data_Object<Intrinsic_T>::from_ruby(value, this->arg_ && this->arg_->isTransfer());
return Data_Object<Intrinsic_T>::from_ruby(value, this->arg_ && this->arg_->isOwner());
}
}

Expand Down
28 changes: 6 additions & 22 deletions rice/Return.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,15 @@ namespace Rice
{
//! Helper for defining Return argument of a method

class Return
class Return: public Arg
{
public:
//! Specifies Ruby should take ownership of the returned value
Return& takeOwnership();
Return();
Return& keepAlive() override;
Return& setValue() override;
Return& setOpaque() override;
Return& takeOwnership() override;

//! Does Ruby own the returned value?
bool isOwner();

//! Specifies the returned value is a Ruby value
Return& setValue();

//! Is the returned value a Ruby value?
bool isValue() const;

//! Tell the returned object to keep alive the receving object
Return& keepAlive();

//! Is the returned value being kept alive?
bool isKeepAlive() const;

private:
bool isKeepAlive_ = false;
bool isOwner_ = false;
bool isValue_ = false;
};
} // Rice

Expand Down
25 changes: 10 additions & 15 deletions rice/Return.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,31 @@

namespace Rice
{
inline Return& Return::takeOwnership()
inline Return::Return(): Arg("Return")
{
this->isOwner_ = true;
return *this;
}

inline bool Return::isOwner()
inline Return& Return::keepAlive()
{
return this->isOwner_;
Arg::keepAlive();
return *this;
}

inline Return& Return::setValue()
{
this->isValue_ = true;
Arg::setValue();
return *this;
}

inline bool Return::isValue() const
{
return this->isValue_;
}

inline Return& Return::keepAlive()
inline Return& Return::setOpaque()
{
this->isKeepAlive_ = true;
Arg::setOpaque();
return *this;
}

inline bool Return::isKeepAlive() const
inline Return& Return::takeOwnership()
{
return this->isKeepAlive_;
Arg::takeOwnership();
return *this;
}
} // Rice
2 changes: 1 addition & 1 deletion rice/cpp_api/shared_methods.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ inline auto& define_method(std::string name, Function_T&& func, const Arg_Ts&...
return *this;
}

//! Define an instance function.
//! Define a function.
/*! The function implementation is a plain function or a static
* member function.
* Rice will automatically convert method method from Ruby to C++ and
Expand Down
4 changes: 3 additions & 1 deletion rice/detail/MethodInfo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace Rice
class MethodInfo
{
public:
MethodInfo() = default;

template <typename...Arg_Ts>
MethodInfo(size_t argCount, const Arg_Ts&...args);

Expand All @@ -22,7 +24,7 @@ namespace Rice
*/
void addArg(const Arg& arg);

Arg& arg(size_t pos);
Arg* arg(size_t pos);

// Iterator support
std::vector<Arg>::iterator begin();
Expand Down
17 changes: 12 additions & 5 deletions rice/detail/MethodInfo.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ namespace Rice
template <typename Arg_T>
inline void MethodInfo::processArg(const Arg_T& arg)
{
if constexpr (std::is_same_v<Arg_T, Arg>)
if constexpr (std::is_same_v<Arg_T, Return>)
{
this->addArg(arg);
this->returnInfo = arg;
}
else
{
this->returnInfo = arg;
this->addArg(arg);
}
}

Expand Down Expand Up @@ -60,9 +60,16 @@ namespace Rice
return std::to_string(required) + std::to_string(optional);
}

inline Arg& MethodInfo::arg(size_t pos)
inline Arg* MethodInfo::arg(size_t pos)
{
return args_[pos];
if (pos < this->args_.size())
{
return &this->args_[pos];
}
else
{
return nullptr;
}
}

inline std::vector<Arg>::iterator MethodInfo::begin()
Expand Down
Loading

0 comments on commit c2fb0de

Please sign in to comment.