145 lines
4.7 KiB
C++
145 lines
4.7 KiB
C++
/**
|
|
* @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
|