no longer need extern "C" { } around epicsExport macros
This commit is contained in:
@ -18,6 +18,11 @@ __This version of EPICS has not been released yet.__
|
|||||||
|
|
||||||
__Add new items below here__
|
__Add new items below here__
|
||||||
|
|
||||||
|
### epicsExport simplifications
|
||||||
|
|
||||||
|
`epicsExportAddress()`, `epicsExportRegistrar()` and `epicsRegisterFunction()`
|
||||||
|
no longer require to be wrapped in `extern "C" { }` in C++ code.
|
||||||
|
|
||||||
### New `dbServerStats()` API for iocStats
|
### New `dbServerStats()` API for iocStats
|
||||||
|
|
||||||
A new routine provides the ability to request channel and client counts from
|
A new routine provides the ability to request channel and client counts from
|
||||||
|
@ -221,6 +221,34 @@ testHarness_SRCS += linkFilterTest.c
|
|||||||
TESTFILES += ../linkFilterTest.db
|
TESTFILES += ../linkFilterTest.db
|
||||||
TESTS += linkFilterTest
|
TESTS += linkFilterTest
|
||||||
|
|
||||||
|
TARGETS += $(COMMON_DIR)/epicsExportTestIoc.dbd
|
||||||
|
DBDDEPENDS_FILES += epicsExportTestIoc.dbd$(DEP)
|
||||||
|
epicsExportTestIoc_DBD += base.dbd
|
||||||
|
epicsExportTestIoc_DBD += epicsExportTest.dbd
|
||||||
|
TESTFILES += $(COMMON_DIR)/epicsExportTestIoc.dbd ../epicsExportTest.db
|
||||||
|
|
||||||
|
TESTLIBRARY += epicsExportTestLib
|
||||||
|
epicsExportTestLib_SRCS += epicsExportTest.c
|
||||||
|
epicsExportTestLib_LIBS += dbCore Com
|
||||||
|
|
||||||
|
TESTPROD_HOST += epicsExportTest
|
||||||
|
TESTS += epicsExportTest
|
||||||
|
TARGETS += epicsExportTest$(OBJ)
|
||||||
|
epicsExportTest_SRCS += epicsExportTestMain.c
|
||||||
|
epicsExportTest_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp
|
||||||
|
epicsExportTest_LIBS = epicsExportTestLib
|
||||||
|
|
||||||
|
TESTLIBRARY += epicsExportTestxxLib
|
||||||
|
epicsExportTestxxLib_SRCS += epicsExportTestxx.cpp
|
||||||
|
epicsExportTestxxLib_LIBS += dbCore Com
|
||||||
|
|
||||||
|
TESTPROD_HOST += epicsExportTestxx
|
||||||
|
TESTS += epicsExportTestxx
|
||||||
|
TARGETS += epicsExportTestxx$(OBJ)
|
||||||
|
epicsExportTestxx_SRCS += epicsExportTestMain.c
|
||||||
|
epicsExportTestxx_SRCS += epicsExportTestIoc_registerRecordDeviceDriver.cpp
|
||||||
|
epicsExportTestxx_LIBS = epicsExportTestxxLib
|
||||||
|
|
||||||
# These are compile-time tests, no need to link or run
|
# These are compile-time tests, no need to link or run
|
||||||
TARGETS += dbHeaderTest$(OBJ)
|
TARGETS += dbHeaderTest$(OBJ)
|
||||||
TARGET_SRCS += dbHeaderTest.cpp
|
TARGET_SRCS += dbHeaderTest.cpp
|
||||||
|
146
modules/database/test/std/rec/epicsExportTest.c
Normal file
146
modules/database/test/std/rec/epicsExportTest.c
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*************************************************************************\
|
||||||
|
* Copyright (c) 2025 Dirk Zimoch
|
||||||
|
* SPDX-License-Identifier: EPICS
|
||||||
|
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||||
|
* in file LICENSE that is included with this distribution.
|
||||||
|
\*************************************************************************/
|
||||||
|
|
||||||
|
/* Compile and link test for epicsExport.h
|
||||||
|
*
|
||||||
|
* Test if macros eppicsExportAddr, epicsExportRegistrar and epicsRegisterFunction
|
||||||
|
* expand to valid C and C++ code, in the latter case regardless if
|
||||||
|
* wrapped with extern "C" {} or not.
|
||||||
|
* Also test that those macros have the intended effect in both cases.
|
||||||
|
*
|
||||||
|
* This file is compiled directly as C
|
||||||
|
* and included from epicsExportTestxx.cpp as C++
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <epicsUnitTest.h>
|
||||||
|
#include <dbUnitTest.h>
|
||||||
|
#include <testMain.h>
|
||||||
|
#include <dbAccess.h>
|
||||||
|
#include <longinRecord.h>
|
||||||
|
#include <aSubRecord.h>
|
||||||
|
#include <iocsh.h>
|
||||||
|
#include <registryFunction.h>
|
||||||
|
#include <epicsExport.h>
|
||||||
|
|
||||||
|
int i1, i2;
|
||||||
|
volatile int v1=10, v2=20;
|
||||||
|
const int c1=100, c2=200;
|
||||||
|
|
||||||
|
static long myReadLongin(struct dbCommon* prec)
|
||||||
|
{
|
||||||
|
struct longinRecord* pli =
|
||||||
|
#ifdef __cplusplus
|
||||||
|
reinterpret_cast<struct longinRecord*>(prec);
|
||||||
|
#else
|
||||||
|
(struct longinRecord*)prec;
|
||||||
|
#endif
|
||||||
|
pli->val=5;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Also test cast from user-specific const mydset to dset */
|
||||||
|
const struct mydset {
|
||||||
|
long number;
|
||||||
|
long (*report)(int);
|
||||||
|
long (*init)(int);
|
||||||
|
long (*init_record)(struct dbCommon*);
|
||||||
|
long (*get_ioint_info)(int, struct dbCommon*, IOSCANPVT*);
|
||||||
|
long (*process)(struct dbCommon*);
|
||||||
|
long (*something_else)(struct dbCommon*);
|
||||||
|
} dset1 = {
|
||||||
|
6,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
myReadLongin,
|
||||||
|
NULL
|
||||||
|
}, dset2 = {
|
||||||
|
6,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
myReadLongin,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static void registrar1() {
|
||||||
|
i1++;
|
||||||
|
testPass("registrar1 executed");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void registrar2() {
|
||||||
|
i2++;
|
||||||
|
testPass("registrar2 executed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Also test cast from int(*)() to REGISTRAR */
|
||||||
|
static int registrar3() {
|
||||||
|
i1++;
|
||||||
|
testPass("registrar3 executed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int registrar4() {
|
||||||
|
i2++;
|
||||||
|
testPass("registrar4 executed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test both, native (potentially C++) and extern "C" functions */
|
||||||
|
static long aSubInit1(aSubRecord* prec) {
|
||||||
|
*(epicsInt32*)prec->a = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long aSubProc1(aSubRecord* prec) {
|
||||||
|
*(epicsInt32*)prec->b = 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
static long aSubInit2(aSubRecord* prec) {
|
||||||
|
*(epicsInt32*)prec->a = 3;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static long aSubProc2(aSubRecord* prec) {
|
||||||
|
*(epicsInt32*)prec->b = 4;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Test without wrapping */
|
||||||
|
epicsExportAddress(int, i1);
|
||||||
|
epicsExportAddress(int, v1);
|
||||||
|
epicsExportAddress(int, c1);
|
||||||
|
epicsExportAddress(dset, dset1);
|
||||||
|
epicsExportRegistrar(registrar1);
|
||||||
|
epicsExportRegistrar(registrar3);
|
||||||
|
epicsRegisterFunction(aSubInit1);
|
||||||
|
epicsRegisterFunction(aSubProc1);
|
||||||
|
|
||||||
|
/* In C++ test wrapped in extern "C" {} */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
epicsExportAddress(int, i2);
|
||||||
|
epicsExportAddress(int, v2);
|
||||||
|
epicsExportAddress(int, c2);
|
||||||
|
epicsExportAddress(dset, dset2);
|
||||||
|
epicsExportRegistrar(registrar2);
|
||||||
|
epicsExportRegistrar(registrar4);
|
||||||
|
epicsRegisterFunction(aSubInit2);
|
||||||
|
epicsRegisterFunction(aSubProc2);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
24
modules/database/test/std/rec/epicsExportTest.db
Normal file
24
modules/database/test/std/rec/epicsExportTest.db
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
record(longin, "li1") {
|
||||||
|
field(DTYP, "dset1")
|
||||||
|
field(VAL, "-1")
|
||||||
|
}
|
||||||
|
record(longin, "li2") {
|
||||||
|
field(DTYP, "dset2")
|
||||||
|
field(VAL, "-2")
|
||||||
|
}
|
||||||
|
record(aSub, "asub1") {
|
||||||
|
field(FTA, "LONG")
|
||||||
|
field(FTB, "LONG")
|
||||||
|
field(INPA, "-1")
|
||||||
|
field(INPB, "-2")
|
||||||
|
field(INAM, "aSubInit1")
|
||||||
|
field(SNAM, "aSubProc1")
|
||||||
|
}
|
||||||
|
record(aSub, "asub2") {
|
||||||
|
field(FTA, "LONG")
|
||||||
|
field(FTB, "LONG")
|
||||||
|
field(INPA, "-3")
|
||||||
|
field(INPB, "-4")
|
||||||
|
field(INAM, "aSubInit2")
|
||||||
|
field(SNAM, "aSubProc2")
|
||||||
|
}
|
16
modules/database/test/std/rec/epicsExportTest.dbd
Normal file
16
modules/database/test/std/rec/epicsExportTest.dbd
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
variable(i1,int)
|
||||||
|
variable(i2,int)
|
||||||
|
variable(v1,int)
|
||||||
|
variable(v2,int)
|
||||||
|
variable(c1,int)
|
||||||
|
variable(c2,int)
|
||||||
|
device(longin, CONSTANT, dset1, "dset1")
|
||||||
|
device(longin, CONSTANT, dset2, "dset2")
|
||||||
|
registrar(registrar1)
|
||||||
|
registrar(registrar2)
|
||||||
|
registrar(registrar3)
|
||||||
|
registrar(registrar4)
|
||||||
|
function(aSubInit1)
|
||||||
|
function(aSubProc1)
|
||||||
|
function(aSubInit2)
|
||||||
|
function(aSubProc2)
|
95
modules/database/test/std/rec/epicsExportTestMain.c
Normal file
95
modules/database/test/std/rec/epicsExportTestMain.c
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*************************************************************************\
|
||||||
|
* Copyright (c) 2025 Dirk Zimoch
|
||||||
|
* SPDX-License-Identifier: EPICS
|
||||||
|
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||||
|
* in file LICENSE that is included with this distribution.
|
||||||
|
\*************************************************************************/
|
||||||
|
|
||||||
|
/* Compile and link test for epicsExport.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <epicsUnitTest.h>
|
||||||
|
#include <dbUnitTest.h>
|
||||||
|
#include <testMain.h>
|
||||||
|
#include <dbAccess.h>
|
||||||
|
#include <iocsh.h>
|
||||||
|
#include <epicsExport.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
#endif
|
||||||
|
int epicsExportTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||||
|
|
||||||
|
static int* testVarEquals(const char* name, int expected)
|
||||||
|
{
|
||||||
|
const iocshVarDef *var;
|
||||||
|
int *p;
|
||||||
|
|
||||||
|
var = iocshFindVariable(name);
|
||||||
|
if (!var) {
|
||||||
|
testFail("Cannot access variable %s", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (var->type != iocshArgInt) {
|
||||||
|
testFail("Variable %s has wrong type", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
p = (int*)(var->pval);
|
||||||
|
testOk(*p == expected, "Variable %s == %d", name, expected);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
MAIN(epicsExportTest)
|
||||||
|
{
|
||||||
|
int *p1, *p2;
|
||||||
|
|
||||||
|
testPlan(31);
|
||||||
|
testdbPrepare();
|
||||||
|
testdbReadDatabase("epicsExportTestIoc.dbd", 0, 0);
|
||||||
|
testOk(epicsExportTestIoc_registerRecordDeviceDriver(pdbbase)==0, "registerRecordDeviceDriver");
|
||||||
|
|
||||||
|
testDiag("Testing if dsets and functions are found");
|
||||||
|
testdbReadDatabase("epicsExportTest.db", 0, 0);
|
||||||
|
testIocInitOk();
|
||||||
|
|
||||||
|
testDiag("Testing if dsets work correctly");
|
||||||
|
testdbGetFieldEqual("li1", DBF_LONG, -1);
|
||||||
|
testdbGetFieldEqual("li2", DBF_LONG, -2);
|
||||||
|
testdbPutFieldOk("li1.PROC", DBF_LONG, 1);
|
||||||
|
testdbPutFieldOk("li2.PROC", DBF_LONG, 1);
|
||||||
|
testdbGetFieldEqual("li1", DBF_LONG, 5);
|
||||||
|
testdbGetFieldEqual("li2", DBF_LONG, 5);
|
||||||
|
|
||||||
|
testDiag("Testing if functions work correctly");
|
||||||
|
testdbGetFieldEqual("asub1.A", DBF_LONG, 1);
|
||||||
|
testdbGetFieldEqual("asub1.B", DBF_LONG, -2);
|
||||||
|
testdbGetFieldEqual("asub2.A", DBF_LONG, 3);
|
||||||
|
testdbGetFieldEqual("asub2.B", DBF_LONG, -4);
|
||||||
|
testdbPutFieldOk("asub1.PROC", DBF_LONG, 1);
|
||||||
|
testdbPutFieldOk("asub2.PROC", DBF_LONG, 1);
|
||||||
|
testdbGetFieldEqual("asub1.A", DBF_LONG, 1);
|
||||||
|
testdbGetFieldEqual("asub1.B", DBF_LONG, 2);
|
||||||
|
testdbGetFieldEqual("asub2.A", DBF_LONG, 3);
|
||||||
|
testdbGetFieldEqual("asub2.B", DBF_LONG, 4);
|
||||||
|
|
||||||
|
testDiag("Testing if variable access works");
|
||||||
|
p1 = testVarEquals("i1", 2);
|
||||||
|
p2 = testVarEquals("i2", 2);
|
||||||
|
testVarEquals("v1", 10);
|
||||||
|
testVarEquals("v2", 20);
|
||||||
|
testVarEquals("c1", 100);
|
||||||
|
testVarEquals("c2", 200);
|
||||||
|
|
||||||
|
if (p1 && p2) {
|
||||||
|
testDiag("Testing if variables are accessible from iocsh");
|
||||||
|
testOk(iocshCmd("var i1,4") == 0, "Setting i1 = 4 in iocsh");
|
||||||
|
testOk(iocshCmd("var i2,5") == 0, "Setting i2 = 5 in iocsh");
|
||||||
|
testOk(*p1==4, "Variable i1 == 4");
|
||||||
|
testOk(*p2==5, "Variable i2 == 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
testIocShutdownOk();
|
||||||
|
testdbCleanup();
|
||||||
|
return testDone();
|
||||||
|
}
|
2
modules/database/test/std/rec/epicsExportTestxx.cpp
Normal file
2
modules/database/test/std/rec/epicsExportTestxx.cpp
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// just compile as c++
|
||||||
|
#include "epicsExportTest.c"
|
@ -41,6 +41,10 @@ extern "C" {
|
|||||||
|
|
||||||
typedef void (*REGISTRAR)(void);
|
typedef void (*REGISTRAR)(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define EPICS_EXPORT_POBJ(typ, obj) pvar_ ## typ ## _ ## obj
|
#define EPICS_EXPORT_POBJ(typ, obj) pvar_ ## typ ## _ ## obj
|
||||||
#define EPICS_EXPORT_PFUNC(fun) EPICS_EXPORT_POBJ(func, fun)
|
#define EPICS_EXPORT_PFUNC(fun) EPICS_EXPORT_POBJ(func, fun)
|
||||||
|
|
||||||
@ -75,12 +79,16 @@ typedef void (*REGISTRAR)(void);
|
|||||||
*
|
*
|
||||||
* \param typ Object's data type.
|
* \param typ Object's data type.
|
||||||
* \param obj Object's name.
|
* \param obj Object's name.
|
||||||
*
|
|
||||||
* \note C++ code needs to wrap with @code extern "C" { } @endcode
|
|
||||||
*/
|
*/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define epicsExportAddress(typ, obj) \
|
||||||
|
extern "C" { epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); } \
|
||||||
|
epicsShareDef typ *EPICS_EXPORT_POBJ(typ, obj) = (typ *) (char *) &obj
|
||||||
|
#else
|
||||||
#define epicsExportAddress(typ, obj) \
|
#define epicsExportAddress(typ, obj) \
|
||||||
epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); \
|
epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); \
|
||||||
epicsShareDef typ *EPICS_EXPORT_POBJ(typ, obj) = (typ *) (char *) &obj
|
epicsShareDef typ *EPICS_EXPORT_POBJ(typ, obj) = (typ *) (char *) &obj
|
||||||
|
#endif
|
||||||
|
|
||||||
/** \brief Declare a registrar function for exporting.
|
/** \brief Declare a registrar function for exporting.
|
||||||
*
|
*
|
||||||
@ -94,11 +102,15 @@ typedef void (*REGISTRAR)(void);
|
|||||||
\endcode
|
\endcode
|
||||||
*
|
*
|
||||||
* \param fun Registrar function's name.
|
* \param fun Registrar function's name.
|
||||||
*
|
|
||||||
* \note C++ code needs to wrap with @code extern "C" { } @endcode
|
|
||||||
*/
|
*/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define epicsExportRegistrar(fun) \
|
||||||
|
extern "C" { extern epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(fun); } \
|
||||||
|
REGISTRAR EPICS_EXPORT_PFUNC(fun) = reinterpret_cast<REGISTRAR>(&fun)
|
||||||
|
#else
|
||||||
#define epicsExportRegistrar(fun) \
|
#define epicsExportRegistrar(fun) \
|
||||||
epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(fun) = (REGISTRAR) &fun
|
epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(fun) = (REGISTRAR) &fun
|
||||||
|
#endif
|
||||||
|
|
||||||
/** \brief Declare and register a function for exporting.
|
/** \brief Declare and register a function for exporting.
|
||||||
*
|
*
|
||||||
@ -111,18 +123,21 @@ typedef void (*REGISTRAR)(void);
|
|||||||
\endcode
|
\endcode
|
||||||
*
|
*
|
||||||
* \param fun Function's name
|
* \param fun Function's name
|
||||||
*
|
|
||||||
* \note C++ code needs to wrap with @code extern "C" { } @endcode
|
|
||||||
*/
|
*/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define epicsRegisterFunction(fun) \
|
||||||
|
static void register_func_ ## fun(void) \
|
||||||
|
{ \
|
||||||
|
registryFunctionAdd(#fun, reinterpret_cast<REGISTRYFUNCTION>(fun)); \
|
||||||
|
} \
|
||||||
|
epicsExportRegistrar(register_func_ ## fun)
|
||||||
|
#else
|
||||||
#define epicsRegisterFunction(fun) \
|
#define epicsRegisterFunction(fun) \
|
||||||
static void register_func_ ## fun(void) \
|
static void register_func_ ## fun(void) \
|
||||||
{ \
|
{ \
|
||||||
registryFunctionAdd(#fun, (REGISTRYFUNCTION) fun); \
|
registryFunctionAdd(#fun, (REGISTRYFUNCTION) fun); \
|
||||||
} \
|
} \
|
||||||
epicsExportRegistrar(register_func_ ## fun)
|
epicsExportRegistrar(register_func_ ## fun)
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* INC_epicsExport_H */
|
#endif /* INC_epicsExport_H */
|
||||||
|
Reference in New Issue
Block a user