summaryrefslogtreecommitdiff
path: root/indra/llcommon/tests/classic_callback_test.cpp
blob: c060775c2418bc4ef3749288eb5ac166d5069f62 (plain)
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
/**
 * @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
*****************************************************************************/
// 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;
};

/*****************************************************************************
*   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