Merge remote-tracking branch 'github/epicsExportAddress' into PSI-7.0

This commit is contained in:
2025-03-04 17:55:15 +01:00
8 changed files with 303 additions and 7 deletions

View File

@ -18,7 +18,13 @@ __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 loger require to be wrapped in `extern "C" { }` in C++ code.
### Build system `$(PYTHON)` default changed
## EPICS Release 7.0.9 ## EPICS Release 7.0.9

View File

@ -227,6 +227,29 @@ TARGET_SRCS += dbHeaderTest.cpp
TARGETS += dbHeaderTestxx$(OBJ) TARGETS += dbHeaderTestxx$(OBJ)
TARGET_SRCS += dbHeaderTestxx.cpp TARGET_SRCS += dbHeaderTestxx.cpp
TARGETS += $(COMMON_DIR)/epicsExportTestIoc.dbd
DBDDEPENDS_FILES += epicsExportTestIoc.dbd$(DEP)
epicsExportTestIoc_DBD += base.dbd
epicsExportTestIoc_DBD += epicsExportTest.dbd
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
ifeq ($(T_A),$(EPICS_HOST_ARCH)) ifeq ($(T_A),$(EPICS_HOST_ARCH))
# Host-only tests of softIoc/softIocPVA, caget and pvget (if present) # Host-only tests of softIoc/softIocPVA, caget and pvget (if present)
# Unfortunately hangs too often on CI systems: # Unfortunately hangs too often on CI systems:

View File

@ -0,0 +1,140 @@
/*************************************************************************\
* 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;
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 mydset to dset */
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(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(dset, dset2);
epicsExportRegistrar(registrar2);
epicsExportRegistrar(registrar4);
epicsRegisterFunction(aSubInit2);
epicsRegisterFunction(aSubProc2);
#ifdef __cplusplus
}
#endif

View 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")
}

View File

@ -0,0 +1,12 @@
variable(i1,int)
variable(i2,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)

View File

@ -0,0 +1,74 @@
/*************************************************************************\
* 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>
void epicsExportTestIoc_registerRecordDeviceDriver(struct dbBase *);
MAIN(epicsExportTest)
{
const iocshVarDef *var_i1, *var_i2;
testPlan(26);
testdbPrepare();
testdbReadDatabase("epicsExportTestIoc.dbd", 0, 0);
epicsExportTestIoc_registerRecordDeviceDriver(pdbbase);
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");
var_i1 = iocshFindVariable("i1");
var_i2 = iocshFindVariable("i2");
if (var_i1 && var_i2 && var_i1->type == iocshArgInt && var_i2->type == iocshArgInt) {
int *pi1 = (int*)(var_i1->pval);
int *pi2 = (int*)(var_i2->pval);
testOk(*pi1==2, "Variable i1 counted registrars: %d == 2", *pi1);
testOk(*pi2==2, "Variable i2 counted registrars: %d == 2", *pi2);
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(*pi1==4, "Variable i1: %d == 4", *pi1);
testOk(*pi2==5, "Variable i2: %d == 5", *pi2);
} else {
testFail("Cannot access variables i1, i2");
}
testIocShutdownOk();
testdbCleanup();
return testDone();
}

View File

@ -0,0 +1,2 @@
// just compile as c++
#include "epicsExportTest.c"

View File

@ -75,12 +75,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) = reinterpret_cast<typ *>(&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 +98,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,15 +119,22 @@ 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)
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }