EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  <20212022  2023  2024  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  <20212022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: Extern "C" when defining aSub records
From: Till Straumann via Tech-talk <tech-talk at aps.anl.gov>
To: <tech-talk at aps.anl.gov>
Date: Tue, 2 Feb 2021 18:12:21 +0100
To clarify and illustrate what Michael was saying (= if you use the epics export & registry
facilities you do not have to use extern "C" at all; in fact, your subroutine may even have
static linkage!):

a) Your C++ code you want to be executed from aSub

foo.cc:

#include <aSubRecord.h>
#include <epicsExport.h>
#include <registryFunction.h>
#include <stdio.h>

/* you may use static linkage: */
static void foo(aSubRecord *prec)
{
    printf("ASUB record %s processing\n", prec->name);   
}

/* this creates boiler-plate code to store the address of 'foo()'
 * in the epics registry.
 */
epicsRegisterFunction(foo);
b) DBD snippet (to be included by your application's dbd):

foo.dbd:

    # this snippet ensures the boiler-plate code in foo.cc is
    # *executed* after the dbd is loaded
    registrar(register_func_foo)

c) DB using 'foo':
record(aSub, "FooSub") {
  field(SNAM, "foo")
  field(SCAN, "2 second")
}

HTH
- Till

PS: under the hood, EPICS uses a global variable to publish the address
of 'static void register_func_foo()' to the linker and this way avoids name-mangling
issues.

On 2/2/21 4:54 PM, Michael Davidsaver via Tech-talk wrote:
On 2/2/21 5:04 AM, Simon Rose via Tech-talk wrote:
Hello -

 

I am trying to understand how to register a cpp function for an EPICS aSub record (or something similar). In particular, I am trying to understand where and how to use the extern “C” directive.
While in theory I think that extern "C" could effect calling convention, in
practice it does not.  So the only place extern "C" is needed is around
the epicsExport*() macros.

The actual global symbol name is determined by the epicsExport*,
which is working from a pointer to your function, so c++ name mangling
isn't relevant.

From what I understand, the extern “C” directive is there to tell the c++ compiler to use C-style linking and name mangling. This seems reasonable for any function that is going to be exposed as a function that could be used outside of my specific library, e.g. by an aSub record.

 

The function that is actually calling the aSub function is do_sub from aSubRecord.c. For reference:

 

static long do_sub(aSubRecord *prec)

{

    GENFUNCPTR pfunc = prec->sadr;

    long status;

 

    if (prec->snam[0] == 0)

        return 0;

 

    if (pfunc == NULL) {

        recGblSetSevr(prec, BAD_SUB_ALARM, INVALID_ALARM);

        return S_db_BadSub;

    }

    status = pfunc(prec);

    if (status < 0)

        recGblSetSevr(prec, SOFT_ALARM, prec->brsv);

    else

        prec->udf = FALSE;

 

    return status;

}

 

And the function is called in the line

 

    status = pfunc(prec);

 

Given that this is from a .c file and not a .cpp file, does pfunc need to be defined with extern “C” linkage? The closest analog that I can find is from asyn, specifically asynPortDriver.cpp, which defines a number of functions such as

 

extern "C" {static asynStatus disconnect(void *drvPvt, asynUser *pasynUser)

{

…

}}

 

And these functions are later added into interfaces that, as I understand, are called from other parts of the code, hence their C-style linkages.

 

Unfortunately, the example from the documentation seems to be implicitly in C, so that doesn’t help.

 

In summary, my question is roughly as follows:

 

1. For a function defined in a .cpp file which is to be used as either the INAM or SNAM parameter in an aSub record, does that function need to be declared extern “C”?

2. What about the epicsExportFunction() line? What is the relationship between these two, and do either/both/none of them need to be extern “C”d?

 

Thanks for your help,

 

Cheers,

 

-- 

Simon Rose

Software Engineer

European Spallation Source


    


References:
Extern "C" when defining aSub records Simon Rose via Tech-talk
Re: Extern "C" when defining aSub records Michael Davidsaver via Tech-talk

Navigate by Date:
Prev: Re: setcap and nosuid Siddons, David via Tech-talk
Next: Re: setcap and nosuid Michael Davidsaver via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  <20212022  2023  2024 
Navigate by Thread:
Prev: Re: Extern "C" when defining aSub records Michael Davidsaver via Tech-talk
Next: Questions about Pilatus detectors / latency Ralph Lange via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  <20212022  2023  2024 
ANJ, 02 Feb 2021 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·