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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
/**
* @file classic_callback_test.cpp
* @author Nat Goodspeed
* @date 2021-09-22
* @brief Test ClassicCallback and HeapClassicCallback.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "classic_callback.h"
// STL headers
#include <iostream>
#include <string>
// std headers
// external library headers
// other Linden headers
#include "../test/lltut.h"
/*****************************************************************************
* example callback accepting only (void* userdata)
*****************************************************************************/
// callback_t is part of the specification of someAPI()
typedef void (*callback_t)(const char*, void*);
void someAPI(callback_t callback, void* userdata)
{
callback("called", userdata);
}
// C++ callable I want as the actual callback
struct MyCallback
{
void operator()(const char* msg, void*)
{
mMsg = msg;
}
void callback_with_extra(const std::string& extra, const char* msg)
{
mMsg = extra + ' ' + msg;
}
std::string mMsg;
};
// a function for which I want to bind other data
void callback_with_extra(const std::string& extra, void*)
{
std::cout << "callback_with_extra('" << extra << "', *)\n";
}
/*****************************************************************************
* example callback accepting several params, and void* userdata isn't first
*****************************************************************************/
typedef std::string (*complex_callback)(int, const char*, void*, double);
std::string otherAPI(complex_callback callback, void* userdata)
{
return callback(17, "hello world", userdata, 3.0);
}
// struct into which we can capture complex_callback params
static struct Data
{
void set(int i, const char* s, double f)
{
mi = i;
ms = s;
mf = f;
}
void clear() { set(0, "", 0.0); }
int mi;
std::string ms;
double mf;
} sData;
// C++ callable I want to pass
struct OtherCallback
{
std::string operator()(int num, const char* str, void*, double approx)
{
sData.set(num, str, approx);
return "hello back!";
}
};
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct classic_callback_data
{
};
typedef test_group<classic_callback_data> classic_callback_group;
typedef classic_callback_group::object object;
classic_callback_group classic_callbackgrp("classic_callback");
template<> template<>
void object::test<1>()
{
set_test_name("ClassicCallback");
// engage someAPI(MyCallback())
auto ccb{ makeClassicCallback<callback_t>(MyCallback()) };
someAPI(ccb.get_callback(), ccb.get_userdata());
// Unfortunately, with the side effect confined to the bound
// MyCallback instance, that call was invisible. Bind a reference to a
// named instance by specifying a ref type.
MyCallback mcb;
ClassicCallback<callback_t, void*, MyCallback&> ccb2(mcb);
someAPI(ccb2.get_callback(), ccb2.get_userdata());
ensure_equals("failed to call through ClassicCallback", mcb.mMsg, "called");
// try with HeapClassicCallback
mcb.mMsg.clear();
auto hcbp{ makeHeapClassicCallback<callback_t>(mcb) };
someAPI(hcbp->get_callback(), hcbp->get_userdata());
ensure_equals("failed to call through HeapClassicCallback", mcb.mMsg, "called");
// lambda
// The tricky thing here is that a lambda is an unspecified type, so
// you can't declare a ClassicCallback<signature, void*, that type>.
mcb.mMsg.clear();
auto xcb(
makeClassicCallback<callback_t>(
[&mcb](const char* msg, void*)
{ mcb.callback_with_extra("extra", msg); }));
someAPI(xcb.get_callback(), xcb.get_userdata());
ensure_equals("failed to call lambda", mcb.mMsg, "extra called");
// engage otherAPI(OtherCallback())
OtherCallback ocb;
// Instead of specifying a reference type for the bound CALLBACK, as
// with ccb2 above, you can alternatively move the callable object
// into the ClassicCallback (of course AFTER any other reference).
// That's why OtherCallback uses external data for its observable side
// effect.
auto occb{ makeClassicCallback<complex_callback>(std::move(ocb)) };
std::string result{ otherAPI(occb.get_callback(), occb.get_userdata()) };
ensure_equals("failed to return callback result", result, "hello back!");
ensure_equals("failed to set int", sData.mi, 17);
ensure_equals("failed to set string", sData.ms, "hello world");
ensure_equals("failed to set double", sData.mf, 3.0);
}
} // namespace tut
|