I'd like to know how a composable event-driven design with callbacks can be used in Rust. From my existing experimentation, I have come to suspect that the ownership system in Rust is more suited to top-down procedural code and has problems with references to parent objects that are needed for callbacks in event-driven design.
Essentially, I would like to see the Rust equivalent for the following C++ code. The code implements an EventLoop which dispatches Timer events using a busy-loop, a Timer class with a timer_expired callback, and a User class that schedules a timer in intervals of 500ms.
#include <stdio.h>
#include <assert.h>
#include <list>
#include <chrono>
#include <algorithm>
using namespace std::chrono;
// Wrapping code in System class so we can implement all functions within classes declarations...
template <typename Dummy=void>
struct System {
class Timer;
class EventLoop {
friend class Timer;
private:
std::list<Timer *> m_running_timers;
bool m_iterating_timers;
typename std::list<Timer *>::iterator m_current_timer;
void unlink_timer (Timer *timer)
{
auto it = std::find(m_running_timers.begin(), m_running_timers.end(), timer);
assert(it != m_running_timers.end());
if (m_iterating_timers && it == m_current_timer) {
++m_current_timer;
}
m_running_timers.erase(it);
}
public:
EventLoop()
: m_iterating_timers(false)
{
}
milliseconds get_time()
{
return duration_cast<milliseconds>(system_clock::now().time_since_epoch());
}
void run()
{
while (true) {
milliseconds now = get_time();
m_iterating_timers = true;
m_current_timer = m_running_timers.begin();
while (m_current_timer != m_running_timers.end()) {
Timer *timer = *m_current_timer;
assert(timer->m_running);
if (now >= timer->m_expire_time) {
m_current_timer = m_running_timers.erase(m_current_timer);
timer->m_running = false;
timer->m_callback->timer_expired();
} else {
++m_current_timer;
}
}
m_iterating_timers = false;
}
}
};
struct TimerCallback {
virtual void timer_expired() = 0;
};
class Timer {
friend class EventLoop;
private:
EventLoop *m_loop;
TimerCallback *m_callback;
bool m_running;
milliseconds m_expire_time;
public:
Timer(EventLoop *loop, TimerCallback *callback)
: m_loop(loop), m_callback(callback), m_running(false)
{
}
~Timer()
{
if (m_running) {
m_loop->unlink_timer(this);
}
}
void start (milliseconds delay)
{
stop();
m_running = true;
m_expire_time = m_loop->get_time() + delay;
m_loop->m_running_timers.push_back(this);
}
void stop ()
{
if (m_running) {
m_loop->unlink_timer(this);
m_running = false;
}
}
};
class TimerUser : private TimerCallback {
private:
Timer m_timer;
public:
TimerUser(EventLoop *loop)
: m_timer(loop, this)
{
m_timer.start(milliseconds(500));
}
private:
void timer_expired() override
{
printf("Timer has expired!\n");
m_timer.start(milliseconds(500));
}
};
};
int main ()
{
System<>::EventLoop loop;
System<>::TimerUser user(&loop);
loop.run();
return 0;
}
The code works as standard C++14 and I believe is correct. Note, in a serious implementation I would make the running_timers an intrusive linked-list not a std::list for performance reasons.
Here are some properties of this solution which I need to see in a Rust equivalent:
Timers can be added/removed without restrictions, there are no limitations on how/where a Timer is allocated. For example one can dynamically manage a list of classes each using their own timer.
In a timer_callback, the class being called back has full freedom to access itself, and the timer it is being called from, e.g. to restart it.
In a timer_callback, the class being called also has freedom to delete itself and the timer. The EventLoop understands this possibility.
I can show some things I've tried but I don't think it will be useful. The major pain point I'm having is satisfy the borrowing rules with all the references to parent objects involved for callback traits.
I suspect a RefCell or something similar might be part of a solution, possibly one or more special classes with internal unsafe parts, that allow gluing stuff together. Maybe some parts of reference safety typically provided by Rust could only be guaranteed at runtime by panicking.
Update
I have created a prototype implementation of this in Rust, but it is not safe. Specifically:
Timers and the EventLoop must not be moved. If they are accidentally moves undefined behavior occurs due to use pointers to these. It is not possible to even detect this within Rust.
The callback implementation is a hack but should work. Note that this allows the same object to receive callbacks from two or more Timers, something that is not possible if traits were used for callbacks instead.
The callbacks are unsafe due to use of pointers that point to the object that is to receive the callback.
It is theoretically possible for an object to delete itself, but this actually seems unsafe in Rust. Because if a Timer callback ends up deleting itself, there would be a &self reference at one point that is not valid. The source of this unsafety is the use of pointers for callbacks.
Related
I have a class where I want to call a static method to initialize some things. I tried calling it from the global scope, like in this simplified example:
class MyClass{
static bool initialized = false;
static void init(){
initialized = true;
}
}
MyClass.init();
void main() async {
// Do something useful
}
This results in the following errors:
Functions must have an explicit list of parameters
The name 'MyClass' is already defined
What I find a bit odd is that if I make the init() method return something and assign that result to a variable, it works:
class MyClass{
static bool initialized = false;
static bool init(){
initialized = true;
return true;
}
}
bool _dummy = MyClass.init();
void main() async {
// Do something useful
}
Why is this? And is there a better workaround to call a static void method from the global scope?
The reason is that static variables are lazy evaluated in Dart, so they will first get a value the first time they are accessed.
That means that MyClass.init() in:
bool _dummy = MyClass.init();
Is only being executed when something are trying to access the variable _dummy. It also means that the following in global scope:
MyClass.init();
Does not make any sense in Dart since this code are never going to be executed since there are no reference to the code.
The reason for this design can be found described in the Dart Language Specification:
Static variable declarations with an initializing expression are initialized lazily.
The lazy semantics are given because we do not want a language where one tends to define expensive initialization computations, causing long application startup times. This is especially crucial for Dart, which must support the coding of client applications.
https://dart.dev/guides/language/specifications/DartLangSpec-v2.10.pdf
I have a class in c++ that I'm wrapping into python with pybind11. That class has a std::function, and I'd like to control how the arguments to that function are dealt with (like return value policies). I just can't find the syntax or examples to do this...
class N {
public:
using CallbackType = std::function<void(const OtherClass*)>;
N(CallbackType callback): callback(callback) { }
CallbackType callback;
void doit() {
OtherClass * o = new OtherClass();
callback(o);
}
}
wrapped with
py::class_<OtherClass>(...standard stuff...);
py::class_<N>(m, "N")
.def(py::init<N::CallbackType>(),
py::arg("callback"));
I all works: I can do this in python:
def callback(o):
dosomethingwith(o)
k = N(callback)
, but I'd like to be able to control what happens when callback(o); is called - whether python then will take ownership of the wrapped o variable or not, basically.
I put a printout in the destructor of OtherClass, and as far as I can tell, it never gets called.
OK, I think I figured it out:
Instead of std::function, use a pybind11::function:
using CallbackType = pybind11::function
and then
void doit(const OtherClass &input) {
if (<I want to copy it>) {
callback(pybind11::cast(input, pybind11::return_value_policy::copy));
} else {
callback(pybind11::cast(input, pybind11::return_value_policy::reference));
}
}
I see nothing in pybind11/functional that allows you to change the ownership of the parameters at the point of call, as the struct func_wrapper used is function local, so can not be specialized. You could provide another wrapper yourself, but in the code you can't know whether the callback is a Python function or a bound C++ function (well, technically you can if that bound C++ function is bound by pybind11, but you can't know in general). If the function is C++, then changing Python ownership in the wrapper would be the wrong thing to do, as the temporary proxy may destroy the object even as its payload is stored by the C++ callback.
Do you have control over the implementation of class N? The reason is that by using std::shared_ptr all your ownership problems will automagically evaporate, regardless of whether the callback function is C++ or Python and whether it stores the argument or not. Would work like so, expanding on your example above:
#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
namespace py = pybind11;
class OtherClass {};
class N {
public:
using CallbackType = std::function<void(const std::shared_ptr<OtherClass>&)>;
N(CallbackType callback): callback(callback) { }
CallbackType callback;
void doit() {
auto o = std::make_shared<OtherClass>();
callback(o);
}
};
PYBIND11_MODULE(example, m) {
py::class_<OtherClass, std::shared_ptr<OtherClass>>(m, "OtherClass");
py::class_<N>(m, "N")
.def(py::init<N::CallbackType>(), py::arg("callback"))
.def("doit", &N::doit);
}
I would like to receive a multicast event from the LeapMotion plugin in C++. From their documentation, they mention the following things:
> On Hand Grabbed Event called when a leap hand grab gesture is
> detected. Signature: const FLeapHandData&, Hand, see FLeapHandData
>
> FLeapHandSignature OnHandGrabbed;
So in my .cpp file I added the following:
ALeapMotionGesture::ALeapMotionGesture()
{
PrimaryActorTick.bCanEverTick = true;
Leap = CreateDefaultSubobject<ULeapComponent>(TEXT("Leap"));
}
void ALeapMotionGesture::BeginPlay()
{
Super::BeginPlay();
if (Leap != nullptr) {
FScriptDelegate Delegate;
Delegate.BindUFunction(this, FName("HandGrabbed"));
Leap->OnHandGrabbed.Add(Delegate);
}
}
void ALeapMotionGesture::HandGrabbed(const FLeapHandData& Hand) {
UE_LOG(LogTemp, Warning, TEXT("Hand Grabbed"));
}
As it is the first time I'm using delegates in Unreal/C++, I would like to know how I could make it work?
It compiles fine however I do not receive any events.
Add UFUNCTION() on your function HandGrabbed
Short Answer
Replace:
void ALeapMotionGesture::BeginPlay()
{
Super::BeginPlay();
if (Leap != nullptr) {
FScriptDelegate Delegate;
Delegate.BindUFunction(this, FName("HandGrabbed"));
Leap->OnHandGrabbed.Add(Delegate);
}
}
with:
void ALeapMotionGesture::BeginPlay()
{
Super::BeginPlay();
if (Leap != nullptr) {
Leap->OnHandGrabbed.AddDynamic(this, &ALeapMotionGesture::HandGrabbed);
}
}
Long Answer
ULeapComponent::OnHandGrabbed is a FLeapHandSignature which is declared with DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam.
The LeapMotion README says to consult the Multi-cast documentation, but they are using dynamic delegates, so you actually need to read the Dynamic Delegates documentation. There you will see you should use the AddDynamic helper macro which generates the function name string for you.
Dynamic Delegates make use of helper macros that take care of generating the function name string for you.
From the Dynamic Delegates doc:
Dynamic Delegate Binding
BindDynamic( UserObject, FuncName )
Helper macro for calling BindDynamic() on dynamic delegates.
Automatically generates the function name string.
AddDynamic( UserObject, FuncName )
Helper macro for calling AddDynamic() on dynamic multi-cast delegates.
Automatically generates the function name string.
RemoveDynamic( UserObject, FuncName )
Helper macro for calling RemoveDynamic() on dynamic multi-cast
delegates. Automatically generates the function name string.
Side Note
Dynamic delegates are serialized, which sometimes results in unexpected behavior. For example, you can have delegate functions being called even though your code is no longer calling AddDynamic (because a serialized/saved actor serialized the results of your old code) or you might call AddDynamic even though the deserialization process already did that for you. To be safe, you probably should call RemoveDynamic before AddDynamic. Here's a snippet from FoliageComponent.cpp:
// Ensure delegate is bound (just once)
CapsuleComponent->OnComponentBeginOverlap.RemoveDynamic(this, &AInteractiveFoliageActor::CapsuleTouched);
CapsuleComponent->OnComponentBeginOverlap.AddDynamic(this, &AInteractiveFoliageActor::CapsuleTouched);
(note: this is related to Usage preference between a struct and a class in D language but for a more specific use case)
When writing a D interface to, say, C++ code, SWIG and others do something like this:
class A{
private _A*ptr;//defined as extern(C) elsewhere
this(){ptr=_A_new();}//ditto
this(string s){ptr=_A_new(s);} //ditto
~this(){_A_delete(ptr);} //ditto
void fun(){_A_fun(ptr);}
}
Let's assume no inheritance is needed.
My question is: wouldn't it be preferable to use a struct instead of a class for this?
The pros being:
1) efficiency (stack allocation)
2) ease-of-use (no need to write new everywhere, eg: auto a=A(B(1),C(2)) vs auto a=new A(new B(1),new C(2)) )?
The cons being:
require additional field is_own to handle aliasing via postblit.
What would be the best way to do so?
Is there anything else to worry about?
Here's an attempt:
struct A{
private _A*ptr;
bool is_own;//required for postblit
static A opCall(){//cannot write this() for struct
A a;
a.ptr=_A_new();
a.is_own=true;
return a;
}
this(string s){ptr=_A_new(s); is_own=true;}
~this(){if(is_own) _A_delete(ptr);}
void fun(){_A_fun(ptr);}
this(this){//postblit;
//shallow copy: I don't want to call the C++ copy constructor (expensive or unknown semantics)
is_own=false; //to avoid _A_delete(ptr)
}
}
Note the postblit is necessary for cases when calling functions such as:
myfun(A a){}
I suggest that you read this page. The only functions on C++ classes that you can call in D are virtual functions. That means that
D cannot call C++ special member functions, and vice versa. These include constructors, destructors, conversion operators, operator overloading, and allocators.
And when you declare a C++ class in D, you use an extern(C++) interface. So, your class/struct would look like this
extern(C++) interface A
{
void fun();
}
However, you'd need another extern(C++) function to allocate any objects of type A, since it's C++ code that has to do that as the D code doesn't have access to any of the constructors. You'd also need a way to pass it back to C++ code to be deleted when you're done with it.
Now, if you want to wrap that interface in a type which is going to call the extern(C++) function to construct it and the extern(C++) function to delete it (so that you don't have to worry about doing that manually), then whether you use a class or struct depends entirely on what you're trying to do with it.
A class would be a reference type, which mirrors what the C++ class actually is. So, passing it around would work without you having to do anything special. But if you wanted a guarantee that the wrapped C++ object was freed, you'd have to do so manually, because there's no guarantee that the D class' finalizer would ever be run (and presumably, that's where you'd put the code for calling the C++ function to delete the C++ object). You'd have to either use clear (which will actually be renamed to destroy in the next release of the compiler - dmd 2.060) to destroy the D object (i.e. call its finalizer and handle the destruction of any of its member variables which are value types), or you'd have to call a function on the D object which called the C++ function to delete the C++ object. e.g.
extern(C++) interface A
{
void fun();
}
extern(C++) A createA();
extern(C++) void deleteA(A a);
class Wrapper
{
public:
this()
{
_a = createA();
}
~this()
{
deleteA(_a);
}
auto opDispatch(string name, Args...)(Args args)
{
return mixin("_a." ~ name ~ "(args)");
}
private:
A _a;
}
void main()
{
auto wrapped = new Wrapper();
//do stuff...
//current
clear(wrapped);
//starting with dmd 2.060
//destroy(wrapped);
}
But that does have the downside that if you don't call clear/destroy, and the garbage collector never collects your wrapper object, deleteA will never be called on the C++ object. That may or may not matter. It depends on whether the C++ object really needs its destructor to be called before the program terminates or whether it can just let its memory return to the OS (without its destructor being called) when the program terminates if the GC never needs to collect the wrapper object.
If you want deterministic destruction, then you need a struct. That means that you'll need to worry about making the struct into a reference type. Otherwise, if it gets copied, when one of them is destroyed, the C++ object will be deleted, and the other struct will point to garbage (which it will then try and delete when it gets destroyed). To solve that, you could use std.typecons.RefCounted. Then you get something like
extern(C++) interface A
{
void fun();
}
extern(C++) A createA();
extern(C++) void deleteA(A a);
struct Wrapper
{
public:
static Wrapper opCall()
{
Wrapper retval;
retval._a = createA();
return retval;
}
~this()
{
if(_a !is null)
{
deleteA(_a);
_a = null;
}
}
auto opDispatch(string name, Args...)(Args args)
{
return mixin("_a." ~ name ~ "(args)");
}
private:
A _a;
}
void main()
{
auto wrapped = RefCounted!Wrapper();
//do stuff...
}
You could also define the wrapper so that it has the ref-counting logic in it and avoid RefCounted, but that would definitely be more complicated.
Regardless, I would definitely advise against your suggestion of using a bool to mark whether the wrapper owns the C++ object or not, because if the original wrapper object gets destroyed before all of the copies do, then your copies will point to garbage.
Another option if you did want the C++ object's copy constructor to be used (and therefore treat the C++ object as a value type) would be to add an extern(C++) function which took the C++ object and returned a copy of it and then use it in a postblit.
extern(C++) A copyA(A a);
this(this)
{
if(_a !is null)
_a = copyA(a);
}
Hopefully that makes things clear enough.
I'm trying to use class objects with the Arduino, but I keep running into problems. All I want to do is declare a class and create an object of that class. What would an example be?
On Arduino 1.0, this compiles just fine:
class A
{
public:
int x;
virtual void f() { x=1; }
};
class B : public A
{
public:
int y;
virtual void f() { x=2; }
};
A *a;
B *b;
const int TEST_PIN = 10;
void setup()
{
a=new A();
b=new B();
pinMode(TEST_PIN,OUTPUT);
}
void loop()
{
a->f();
b->f();
digitalWrite(TEST_PIN,(a->x == b->x) ? HIGH : LOW);
}
There is an excellent tutorial on how to create a library for the Arduino platform. A library is basically a class, so it should show you how its all done.
On Arduino you can use classes, but there are a few restrictions:
No new and delete keywords
No exceptions
No libstdc++, hence no standard functions, templates or classes
You also need to make new files for your classes, you can't just declare them in your main sketch. You also will need to close the Arduino IDE when recompiling a library. That is why I use Eclipse as my Arduino IDE.
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230935955 states:
By default, the Arduino IDE and
libraries does not use the operator
new and operator delete. It does
support malloc() and free(). So the
solution is to implement new and
delete operators for yourself, to use
these functions.
Code:
#include <stdlib.h> // for malloc and free
void* operator new(size_t size) { return malloc(size); }
void operator delete(void* ptr) { free(ptr); }
This let's you create objects, e.g.
C* c; // declare variable
c = new C(); // create instance of class C
c->M(); // call method M
delete(c); // free memory
Regards,
tamberg
I created this simple one a while back. The main challenge I had was to create a good build environment - a makefile that would compile and link/deploy everything without having to use the GUI. For the code, here is the header:
class AMLed
{
private:
uint8_t _ledPin;
long _turnOffTime;
public:
AMLed(uint8_t pin);
void setOn();
void setOff();
// Turn the led on for a given amount of time (relies
// on a call to check() in the main loop()).
void setOnForTime(int millis);
void check();
};
And here is the main source
AMLed::AMLed(uint8_t ledPin) : _ledPin(ledPin), _turnOffTime(0)
{
pinMode(_ledPin, OUTPUT);
}
void AMLed::setOn()
{
digitalWrite(_ledPin, HIGH);
}
void AMLed::setOff()
{
digitalWrite(_ledPin, LOW);
}
void AMLed::setOnForTime(int p_millis)
{
_turnOffTime = millis() + p_millis;
setOn();
}
void AMLed::check()
{
if (_turnOffTime != 0 && (millis() > _turnOffTime))
{
_turnOffTime = 0;
setOff();
}
}
It's more prettily formatted here: http://amkimian.blogspot.com/2009/07/trivial-led-class.html
To use, I simply do something like this in the .pde file:
#include "AM_Led.h"
#define TIME_LED 12 // The port for the LED
AMLed test(TIME_LED);
My Webduino library is all based on a C++ class that implements a web server on top of the Arduino Ethernet shield. I defined the whole class in a .h file that any Arduino code can #include. Feel free to look at the code to see how I do it... I ended up just defining it all inline because there's no real reason to separately compile objects with the Arduino IDE.
Can you provide an example of what did not work? As you likely know, the Wiring language is based on C/C++, however, not all of C++ is supported.
Whether you are allowed to create classes in the Wiring IDE, I'm not sure (my first Arduino is in the mail right now). I do know that if you wrote a C++ class, compiled it using AVR-GCC, then loaded it on your Arduino using AVRDUDE, it would work.