Slots/signals from scratch

I'm making a class to listen to a buffer, then when it recieves a word, it broadcasts (emits) that word (signal) to registered functions (slots). My issue is that I'd like to be able to register methods of unknown class types instead of just C functions. This is easy for C:

It's very simplified below to help me concentrate on my question.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class buf_rx
{
public:
  typedef void(*pRxFunc)(int);

private:
  pRxFunc m_process;

public:
  void receive()
  {
    check for new words
    for each new word
      m_process(word);
  }

  void registr ( pRxFunc function )
  {
    m_process = function;
  }
};


To use a pointer to a method, I also need a pointer to the class, but I'm not sure how to save the class type:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class buf_rx
{
private:
  void* m_class;   // The type is wrong here,
  void (*m_method)(int); // I need a class definition before m_method

public:
  void receive()
  {
    check for new words
    for each new word
      m_class->m_process(word);
  }

  template <class T>
  void registr( T* pClass, void(T::*pFunc)(int) )
  { 
    m_class = pClass;
    m_method = pFunc;
  }
};


But this doesn't work. Any ideas? I know Qt's signals/slots would work, but I am trying to keep this code independant of 3rd party libraries.
Last edited on
I'm getting closer, but I don't understand this code:

1
2
3
4
5
6
7
8
9
10
11
12
inline connection connect(std::function<SIGNATURE> target) {
    auto conn = connection::make_connection();
    m_targets->emplace_back(conn, std::move(target));
    return conn;
}

template<typename OBJ, typename ARG1>
inline connection connect(OBJ* obj, void (OBJ::*method)(ARG1 arg1)) {
    assert(obj);
    assert(method);
    return connect([=](ARG1 arg1) { (obj->*method)(arg1); });
}


What is this connect([=](ARG1 arg1) { (obj->*method)(arg1); })?
Last edited on
Thanks Thomas, you're right that's probably the simplest way to go. I was trying to stay away from inheritance because that method seemed very Java-ey to me, but the more I think of it the more I like it. Thanks for the idea:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class buf_listener
{
public: 
  virtual void process(int word) = 0;
};

class buf_rx
{
private:
  buf_listener* m_listener;

public:
  void receive()
  {
    check for new words
    for each new word
      m_listener->process(word);
  }

  void registr ( buf_listener* listener )
  {
    m_listener = listener;
  }
};


I was trying to stay away from inheritance because that method seemed very Java-ey to me, but the more I think of it the more I like it.

I don't care for it. Introduces needless coupling.

I think I'd go for something more along the lines of:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// http://ideone.com/LOiRbs
#include <functional>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

struct buf_rx
{
    using callback_type = std::function<void(std::string)> ;

    void receive(std::istringstream in)
    {
        std::string word;
        while (in >> word)
            for (auto func : _cb)
                func(word);
    }

    void register_cb(callback_type cb_func)
    {
        _cb.push_back(cb_func);
    }

private:
    std::vector<callback_type> _cb;
};

void string_processor(const std::string& s)
{
    std::cout << "string_processor: " << s << '\n';
}

struct some_type
{
    some_type() :_id(id++) {}

    void process_string(const std::string& s) {
        std::cout << "some_type (" << _id << "): " << s << '\n';
    }

private:
    static unsigned id;
    unsigned _id;
};

unsigned some_type::id = 0;

int main()
{
    some_type a;
    some_type b;

    buf_rx buffer;
    buffer.register_cb(string_processor);
    buffer.register_cb([&](std::string s) {a.process_string(s); });
    buffer.register_cb([&](std::string s) {b.process_string(s); });

    buffer.receive(std::istringstream { "a b c d e" });
}


No coupling between classes and you don't have to manufacture a class type to "listen."
Last edited on
That's exactley what I was looking for! Thanks!

I'm not so familiar with the C++11 stuff you've used here, but now I know what I need to do. Thanks!
Topic archived. No new replies allowed.