Callback_07.cpp 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. #include <cstdio>
  2. /*
  3. * https://embeddedartistry.com/blog/2017/07/10/using-a-c-objects-member-function-with-c-style-callbacks/
  4. *
  5. */
  6. // Type definitions for some callback function prototypes
  7. typedef void(cb_func1)(void* priv, int input);
  8. typedef void(cb_func2)(void* priv);
  9. /**
  10. * This is a basic class which has a public function interface that we
  11. * will pass into a callback system.
  12. *
  13. * You can use a seed to ensure that your intended object is the one being called.
  14. */
  15. class FooCB
  16. {
  17. public:
  18. FooCB(void) : seed_(0)
  19. {
  20. }
  21. FooCB(int t) : seed_(t)
  22. {
  23. }
  24. void callback(int input) const
  25. {
  26. printf("FooCB callback, seed+input: %d\n", seed_ + input);
  27. }
  28. void callback2(void) const
  29. {
  30. printf("FooCB callback2, seed: %d\n", seed_);
  31. }
  32. private:
  33. int seed_;
  34. };
  35. /**
  36. * Below are two functions that we can use to demonstrate use of the bounce
  37. * function. The callbacks are setup in the C-style manner, demonstrating two common forms:
  38. * -callback_test takes a function pointer, private data, and input value
  39. * -callback_test_no_input leaves off the input value, providing only private data
  40. */
  41. static void callback_test(cb_func1 cb_func, void* priv, int input)
  42. {
  43. if (cb_func)
  44. {
  45. cb_func(priv, input);
  46. }
  47. }
  48. static void callback_test_no_input(cb_func2 cb_func, void* priv)
  49. {
  50. if (cb_func)
  51. {
  52. cb_func(priv);
  53. }
  54. }
  55. /**
  56. * This is our "bounce" function template.
  57. * This function is intended to be passed to a C-style callback system which takes
  58. * a function pointer and private data pointer as input values. Using C-style callbacks
  59. * is problematic with object member functions, as you need the object's pointer.
  60. *
  61. * This bounce function requires use of the private data pointer for the object's
  62. * instance pointer. Any other provided arguments are forwarded to the object's member
  63. * function, and the return value is passed to the caller.
  64. */
  65. template<class T, class Method, Method m, class... Params>
  66. static auto bounce(void* priv, Params... params)
  67. -> decltype(((*reinterpret_cast<T*>(priv)).*m)(params...))
  68. {
  69. return ((*reinterpret_cast<T*>(priv)).*m)(params...);
  70. }
  71. /**
  72. * Convenience macro to simplify bounce statement usage
  73. */
  74. #define BOUNCE(c, m) bounce<c, decltype(&c::m), &c::m>
  75. int main(void)
  76. {
  77. // We want the callback from this specific instance to be used
  78. FooCB my_foo(2038);
  79. /*
  80. * As you can see, this full bounce declaration is pretty verbose
  81. *
  82. * First argument is the templated bounce function, which specifies:
  83. * - The class we are templating off of
  84. * - The decltype of our callback function
  85. * - The member function pointer we want to call
  86. *
  87. * Next we specify the pointer to the specific object we want to reference
  88. * And finally, we have the input value which will be forwarded to the callback
  89. */
  90. callback_test(&bounce<FooCB, decltype(&FooCB::callback), &FooCB::callback>, &my_foo, 5000);
  91. /*
  92. * Using the bounce macro, we can simplify our typing significantly. Now we
  93. * only need to specify the class and member function inside the macro.
  94. */
  95. callback_test(&BOUNCE(FooCB, callback), &my_foo, 1001);
  96. /**
  97. * The only requirements are that you need to register a function pointer
  98. * and private data pointer! No other input is needed.
  99. */
  100. callback_test_no_input(&BOUNCE(FooCB, callback2), &my_foo);
  101. return 0;
  102. }