Guidelines for implementing apps in C++ #659
ascended121
started this conversation in
General
Replies: 4 comments 1 reply
-
Also, OpenSatKit at one point had a C++ demo app, but that's the only C++ CFS app I've been able to find. Does anyone know of any other publicly available C++ apps? |
Beta Was this translation helpful? Give feedback.
1 reply
-
All well said. I can add one more caveat since writing these. Global and static constructors and destructors. This is an issue with C as well but is more noticeable in C++. Again, this has everything to do with late linking. The problem is unloading applications, as eluded to earlier in the chain.
The POSIX and C++ standards are very clear when it comes to loading dynamic libraries and instantiating objects. When a dynamic library is loaded, the dynamic linker loads all ELF section into memory. Unless you do a lazy load, the linker then attempts to satisfy all symbols with no addresses. If this is a library compiled from C++ it will probably also have a ctor and dtor section. Ctor contain functions that are called to initialize global and static c++ objects. This is where the constructors are called. Dtor contains the global and static destructors. Those that have pieced together a VxWorks toolchain outside of Workbench may have noticed you also have to run the “munger” to populate the ctor and dtor sections or your constructors don’t get called. This behavior is well defined. What is supposed to happen when you unload an object is nebulous and you NEED to understand what happens if you’re writing C++ apps.
The POSIX standard does not require the object actually be removed from memory. The module is just flagged as “unloaded”. Your code and data might still be there. In linux, it usually still is. Anybody that has broken the golden rule of never calling app functions from another app may have noticed it actually still works even when the application being called is “unloaded”. The real problem occurs when we try to RELOAD the same app. Linux is smart and knows the file didn’t really change, so it just flags the module back from unloaded to loaded. The standard is very clear and states that the constructors are to be executed when the module is loaded into memory. Since Linux did not actually reload your module into memory, it will not execute your constructors. All your globals and statics will have the same values and parameters they had when you “unloaded” them.
This also affects C code. Globals and statics are contained in the BSS section, which is required to be zero’d out ON LOAD. When you “reload” your app and the binary didn’t actually change, your BSS will probably contain the same values it did when you unloaded it.
Therefore, my organization has the following coding standards:
1. All global and static variables are explicitly initialized (memset your AppData structures).
2. No classes that are instantiated globally or statically can have explicit constructors or destructors. (Put actual initialization code in some sort of an “init” function. Similarly for your destructor code. Call the “Init” and “Deinit” functions at the appropriate time, and avoid certain patterns. Some patterns prohibit the recommended solution.)
And if you try loading a NEW app image compiled from updated code, on a Linux kernel built for embedded use, things can get really freaky on some platforms.
…Sent from my iPhone
On Mar 21, 2023, at 14:05, Isaac Rowe ***@***.***> wrote:
There's been some prior discussion of this on the cFS-community mailing list. Unfortunately, I think the archive requires NASA authentication. I'm going to post some quotes from other folks:
***@***.***/thread/OANSPI46GVMPNNNSB467P5LD23F274GO/:
@mbenson1:
In my experience, base classes and especially templates can be problematic, due to the late linking nature of CFS. If you have base classes or templates shared between applications, then this applies. If not, you won’t have a problem. Base classes between applications will collide. He first application loaded won’t have any issues. When the second application is loaded, the symbols will collide. VxWorks will just error outright. Linux will just use the previously loaded symbol, which may or may not be what is intended. We put the base class code in CFS Libs. Just be very careful if you think any dependent application could have a development life cycle outside of the other applications. Even though applications can physically be compiled and loaded independently, of a shared base class is modified, all the applications that use that base class should also be recompiled even if the code has not changed. You could have strange link errors at runtime otherwise.
Templates are trickier due to how the compiler generates the concrete code from the templates. You can’t put the templates code in a lib. It won’t do any good. Dropping a .h template file in the lib won’t do any good since the compiler will automatically generate the concrete .c files from the template for each of the applications that use it. Each application will have its own copy. If the template types are the same, symbols will collide. You might be able to prevent symbol collisions by making all symbols static. Some compilers, like Green Hills, have modes where the generated concrete .c files from the template can be retained and put into a CFS lib. But this makes the code very toolchain specific. I normally just avoid templates in CFS code unless templates are not shared and I know positively beyond the shadow of a doubt that my code won’t be used by other people or on other projects, which is almost never. This was a big problem for a templated math library. We manually instantiated the templates into concrete code.
Aside from that, just the normal linkage issues. Some minor coding standards mismatches, depending on the toolchain, by easily corrected.
CFS doesn't have any unique issues with C++ name mangling that any other C
application doesn't have. The unique issues are due to the late linking
nature of application loading.
If you only have one application that is using the library, then it's not a
problem since you have no shared symbols between applications to collide
with. If you have multiple applications but they are all using different
template classes such that each symbol results in a uniquely mangled name,
there won't be any collisions and everything will be fine. If you have
multiple applications with the same template class, but no partial
templates and you're running Linux, you won't have any problem, though I
would compile all those applications at the same time, to avoid template
version mismatch. This is because the linux loader just assumes the new
symbol is the same as the old symbol, though I can positively confirm that
it only looks at the symbol name. If the function itself is different,
you'll get strange things happening. This is also why I say "no partial
templates". If you have partial templates, I'm not sure.
I've wrestled with this template issue for a long time. There are
definitely ways to get them to work. Odds are other people have used
templates in the past and not have had problems. I didn't think of any of
these problems until I was forced to when I wrote the first object oriented
CFS applications on the Orion program, and immediately discovered that
VxWorks loader just flat out rejected it when symbols collided and Linux
allowed it. I had to dig deep in the bowels of the language, toolchain,
and the loader to understand where the future pitfalls would be. I know
there are multiple ways around the various issues, but I haven't found an
easy to use magic bullet that works in all situations. I would love to
hear from others that have. Our public repository contains 25 object
oriented C++ applications, containing about 40,000 lines of code (
https://github.com/windhoverlabs/airliner). We completely rewrote one
template based math library to remove the templates. It's at
https://github.com/WindhoverLabs/airliner/tree/develop/apps/px4lib/fsw/src/m....
It sucks, but we had multiple applications using it and I needed a solution
that I knew did not have any holes.
A better solution, if I could find a tool to do it, would be similar to one
of the modes that the Green Hills compiler is capable of building
templates. Basically all any of the compilers do is use the template .h
files literally as a template to automatically generate a .cpp file first,
then it compiles it. Usually it either deletes the generated .cpp file or
the more modern tools don't even generate the .cpp file on the file
system. It all happens internally. Green Hills has a mode where it will
generate the .cpp file, but keep it there after the build is done. There
may be a way to make gnu tools do that. I just don't know how. But if you
could just keep the .cpp files that the compiler generates and either
convert the template .h to a concrete .h or tell the compiler to not
generate and compile the concrete .cpp in your applications, you could then
just collect them together into a single CFE library. Then you could
maintain your templated code independently from the concrete applications.
Partial templates might still be an issue. I'm not entirely certain how
those are generated by the precompiler or linked at run time.
Again, none of this is really a CFS unique thing. Anything that supports
late linking is susceptible.
@iamthad:
Linkage and dynamic loading issues seem to be the majority of problems
that people have seen with C++ apps in CFS.
As well as the issues Mathew discusses above, note that if you:
wish to be able to unload your app
are using an OSAL that implements modules using dlopen and dlclose
(e.g. POSIX)
are using the GNU C++ toolchain
then you should be aware of the STB_GNU_UNIQUE symbol binding type
that is used to ensure certain requirements in the C++ language such
as "vague linkage".
If your app contains any symbols with this binding, it will not be
unloaded by dlclose.
More information:
The rationale for adding the feature:
https://gcc.gnu.org/legacy-ml/gcc-patches/2009-07/msg01240.html
Some bug reports and discussion:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66830
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71971
https://bugzilla.redhat.com/show_bug.cgi?id=1215452
Allen Brown, Odyssey Space Research:
In addition to what Matt and Thad have said, I had success when placing an
entire c++ cFS app in its own namespace to protect against symbol
collisions. Only the C entry point wasn't mangled.
@mbenson1:
And the task delete callback, if used. Those are the only two symbols that
should ever be called up from the framework to the application.
AB:
That's right. Thanks. Some apps won't need that callback, especially with
proper C++ RAII on classes you control. But anything is possible when
dealing with external libraries and/or system resources.
What I haven't done with C++ cFS apps is attempt to use the existing cFS
apps that perform a symbol lookup, like MM, MD, and the CFS_LIB. With the
C++ name mangling and the OSAL symbol length limit these features may have
problems. You should review your dev and flight troubleshooting use cases.
***@***.***/thread/3BRMUGGE2TOLGKIP6NIQNZV4UWTJIUAF/:
Chuck Claunch, NASA JSC/Jacobs technology:
We (an autonomy project for Gateway out of JSC/ER4) have successfully used several C++ libraries in our CFS apps. Our approach was to use a C wrapper as an interface to our C++ objects. The C++ objects do all the work and the C wrapper just does the CFS I/O and scheduling. The only hiccup was the annoyance of having to build all the dependencies as 32-bit.
(editor's note: the 32-bit restriction is no longer relevant)
@mbenson1:
I've used C++ libraries and written CFS apps in C++ many times. With one
exception, there isn't anything different using C++ libraries in CFS than
using C++ in any C application. The usual C++ to C linkage issues still
apply so just follow best practices.
The only exception has nothing to do with C vs. C++, but has everything to
do with libraries and late linking apps. You can get unexpected side
effects if you link the same library in multiple applications. The dynamic
loader will have multiple identically named symbols to load and link in.
The first one won't have any problems, but the second application that is
loaded in with the same library will either conflict with the already
loaded symbol and abort the load (VxWorks), or assume its the same symbol
and link the one that's already loaded (Linux). To avoid it when multiple
applications require the library, either link the library in the core
binary or to a CFE Library (CFE_LIB) and load the library before your
application is loaded.
@jphickey :
A few known caveats:
The CFE header files do not currently include a conditional 'extern
"C"' block around the declarations. This is required for proper linkage
to C++. As a workaround you can put this at the point you #include them
in your C++ source.
Exception handling can get tricky when integrating C and C++ code,
when the stack unwinding has to cross between the languages. Not every
compiler/library/toolchain combo handles this. You may have to disable
exception handling (i.e. -fno-exceptions etc). The GNU docs have a
good overview here:
https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_exceptions.html
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you were mentioned.
|
Beta Was this translation helpful? Give feedback.
0 replies
-
Lastly, don’t be afraid of C++ code in CFS. When used effectively, the benefits can outweigh the problems. Stick to certain guidelines and you’ll be fine.
…Sent from my iPhone
On Mar 24, 2023, at 12:24, ***@***.*** wrote:
All well said. I can add one more caveat since writing these. Global and static constructors and destructors. This is an issue with C as well but is more noticeable in C++. Again, this has everything to do with late linking. The problem is unloading applications, as eluded to earlier in the chain.
The POSIX and C++ standards are very clear when it comes to loading dynamic libraries and instantiating objects. When a dynamic library is loaded, the dynamic linker loads all ELF section into memory. Unless you do a lazy load, the linker then attempts to satisfy all symbols with no addresses. If this is a library compiled from C++ it will probably also have a ctor and dtor section. Ctor contain functions that are called to initialize global and static c++ objects. This is where the constructors are called. Dtor contains the global and static destructors. Those that have pieced together a VxWorks toolchain outside of Workbench may have noticed you also have to run the “munger” to populate the ctor and dtor sections or your constructors don’t get called. This behavior is well defined. What is supposed to happen when you unload an object is nebulous and you NEED to understand what happens if you’re writing C++ apps.
The POSIX standard does not require the object actually be removed from memory. The module is just flagged as “unloaded”. Your code and data might still be there. In linux, it usually still is. Anybody that has broken the golden rule of never calling app functions from another app may have noticed it actually still works even when the application being called is “unloaded”. The real problem occurs when we try to RELOAD the same app. Linux is smart and knows the file didn’t really change, so it just flags the module back from unloaded to loaded. The standard is very clear and states that the constructors are to be executed when the module is loaded into memory. Since Linux did not actually reload your module into memory, it will not execute your constructors. All your globals and statics will have the same values and parameters they had when you “unloaded” them.
This also affects C code. Globals and statics are contained in the BSS section, which is required to be zero’d out ON LOAD. When you “reload” your app and the binary didn’t actually change, your BSS will probably contain the same values it did when you unloaded it.
Therefore, my organization has the following coding standards:
1. All global and static variables are explicitly initialized (memset your AppData structures).
2. No classes that are instantiated globally or statically can have explicit constructors or destructors. (Put actual initialization code in some sort of an “init” function. Similarly for your destructor code. Call the “Init” and “Deinit” functions at the appropriate time, and avoid certain patterns. Some patterns prohibit the recommended solution.)
And if you try loading a NEW app image compiled from updated code, on a Linux kernel built for embedded use, things can get really freaky on some platforms.
Sent from my iPhone
>> On Mar 21, 2023, at 14:05, Isaac Rowe ***@***.***> wrote:
>>
>
> There's been some prior discussion of this on the cFS-community mailing list. Unfortunately, I think the archive requires NASA authentication. I'm going to post some quotes from other folks:
>
> ***@***.***/thread/OANSPI46GVMPNNNSB467P5LD23F274GO/:
>
> @mbenson1:
>
> In my experience, base classes and especially templates can be problematic, due to the late linking nature of CFS. If you have base classes or templates shared between applications, then this applies. If not, you won’t have a problem. Base classes between applications will collide. He first application loaded won’t have any issues. When the second application is loaded, the symbols will collide. VxWorks will just error outright. Linux will just use the previously loaded symbol, which may or may not be what is intended. We put the base class code in CFS Libs. Just be very careful if you think any dependent application could have a development life cycle outside of the other applications. Even though applications can physically be compiled and loaded independently, of a shared base class is modified, all the applications that use that base class should also be recompiled even if the code has not changed. You could have strange link errors at runtime otherwise.
>
> Templates are trickier due to how the compiler generates the concrete code from the templates. You can’t put the templates code in a lib. It won’t do any good. Dropping a .h template file in the lib won’t do any good since the compiler will automatically generate the concrete .c files from the template for each of the applications that use it. Each application will have its own copy. If the template types are the same, symbols will collide. You might be able to prevent symbol collisions by making all symbols static. Some compilers, like Green Hills, have modes where the generated concrete .c files from the template can be retained and put into a CFS lib. But this makes the code very toolchain specific. I normally just avoid templates in CFS code unless templates are not shared and I know positively beyond the shadow of a doubt that my code won’t be used by other people or on other projects, which is almost never. This was a big problem for a templated math library. We manually instantiated the templates into concrete code.
>
> Aside from that, just the normal linkage issues. Some minor coding standards mismatches, depending on the toolchain, by easily corrected.
>
> CFS doesn't have any unique issues with C++ name mangling that any other C
> application doesn't have. The unique issues are due to the late linking
> nature of application loading.
>
> If you only have one application that is using the library, then it's not a
> problem since you have no shared symbols between applications to collide
> with. If you have multiple applications but they are all using different
> template classes such that each symbol results in a uniquely mangled name,
> there won't be any collisions and everything will be fine. If you have
> multiple applications with the same template class, but no partial
> templates and you're running Linux, you won't have any problem, though I
> would compile all those applications at the same time, to avoid template
> version mismatch. This is because the linux loader just assumes the new
> symbol is the same as the old symbol, though I can positively confirm that
> it only looks at the symbol name. If the function itself is different,
> you'll get strange things happening. This is also why I say "no partial
> templates". If you have partial templates, I'm not sure.
>
> I've wrestled with this template issue for a long time. There are
> definitely ways to get them to work. Odds are other people have used
> templates in the past and not have had problems. I didn't think of any of
> these problems until I was forced to when I wrote the first object oriented
> CFS applications on the Orion program, and immediately discovered that
> VxWorks loader just flat out rejected it when symbols collided and Linux
> allowed it. I had to dig deep in the bowels of the language, toolchain,
> and the loader to understand where the future pitfalls would be. I know
> there are multiple ways around the various issues, but I haven't found an
> easy to use magic bullet that works in all situations. I would love to
> hear from others that have. Our public repository contains 25 object
> oriented C++ applications, containing about 40,000 lines of code (
> https://github.com/windhoverlabs/airliner). We completely rewrote one
> template based math library to remove the templates. It's at
> https://github.com/WindhoverLabs/airliner/tree/develop/apps/px4lib/fsw/src/m....
> It sucks, but we had multiple applications using it and I needed a solution
> that I knew did not have any holes.
>
> A better solution, if I could find a tool to do it, would be similar to one
> of the modes that the Green Hills compiler is capable of building
> templates. Basically all any of the compilers do is use the template .h
> files literally as a template to automatically generate a .cpp file first,
> then it compiles it. Usually it either deletes the generated .cpp file or
> the more modern tools don't even generate the .cpp file on the file
> system. It all happens internally. Green Hills has a mode where it will
> generate the .cpp file, but keep it there after the build is done. There
> may be a way to make gnu tools do that. I just don't know how. But if you
> could just keep the .cpp files that the compiler generates and either
> convert the template .h to a concrete .h or tell the compiler to not
> generate and compile the concrete .cpp in your applications, you could then
> just collect them together into a single CFE library. Then you could
> maintain your templated code independently from the concrete applications.
> Partial templates might still be an issue. I'm not entirely certain how
> those are generated by the precompiler or linked at run time.
>
> Again, none of this is really a CFS unique thing. Anything that supports
> late linking is susceptible.
>
> @iamthad:
>
> Linkage and dynamic loading issues seem to be the majority of problems
> that people have seen with C++ apps in CFS.
>
> As well as the issues Mathew discusses above, note that if you:
>
> wish to be able to unload your app
> are using an OSAL that implements modules using dlopen and dlclose
> (e.g. POSIX)
> are using the GNU C++ toolchain
> then you should be aware of the STB_GNU_UNIQUE symbol binding type
> that is used to ensure certain requirements in the C++ language such
> as "vague linkage".
>
> If your app contains any symbols with this binding, it will not be
> unloaded by dlclose.
>
> More information:
>
> The rationale for adding the feature:
> https://gcc.gnu.org/legacy-ml/gcc-patches/2009-07/msg01240.html
> Some bug reports and discussion:
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66830
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71971
> https://bugzilla.redhat.com/show_bug.cgi?id=1215452
> Allen Brown, Odyssey Space Research:
>
> In addition to what Matt and Thad have said, I had success when placing an
> entire c++ cFS app in its own namespace to protect against symbol
> collisions. Only the C entry point wasn't mangled.
>
> @mbenson1:
>
> And the task delete callback, if used. Those are the only two symbols that
> should ever be called up from the framework to the application.
>
> AB:
>
> That's right. Thanks. Some apps won't need that callback, especially with
> proper C++ RAII on classes you control. But anything is possible when
> dealing with external libraries and/or system resources.
> What I haven't done with C++ cFS apps is attempt to use the existing cFS
> apps that perform a symbol lookup, like MM, MD, and the CFS_LIB. With the
> C++ name mangling and the OSAL symbol length limit these features may have
> problems. You should review your dev and flight troubleshooting use cases.
>
> ***@***.***/thread/3BRMUGGE2TOLGKIP6NIQNZV4UWTJIUAF/:
>
> Chuck Claunch, NASA JSC/Jacobs technology:
>
> We (an autonomy project for Gateway out of JSC/ER4) have successfully used several C++ libraries in our CFS apps. Our approach was to use a C wrapper as an interface to our C++ objects. The C++ objects do all the work and the C wrapper just does the CFS I/O and scheduling. The only hiccup was the annoyance of having to build all the dependencies as 32-bit.
>
> (editor's note: the 32-bit restriction is no longer relevant)
>
> @mbenson1:
>
> I've used C++ libraries and written CFS apps in C++ many times. With one
> exception, there isn't anything different using C++ libraries in CFS than
> using C++ in any C application. The usual C++ to C linkage issues still
> apply so just follow best practices.
>
> The only exception has nothing to do with C vs. C++, but has everything to
> do with libraries and late linking apps. You can get unexpected side
> effects if you link the same library in multiple applications. The dynamic
> loader will have multiple identically named symbols to load and link in.
> The first one won't have any problems, but the second application that is
> loaded in with the same library will either conflict with the already
> loaded symbol and abort the load (VxWorks), or assume its the same symbol
> and link the one that's already loaded (Linux). To avoid it when multiple
> applications require the library, either link the library in the core
> binary or to a CFE Library (CFE_LIB) and load the library before your
> application is loaded.
>
> @jphickey :
>
> A few known caveats:
>
> The CFE header files do not currently include a conditional 'extern
> "C"' block around the declarations. This is required for proper linkage
> to C++. As a workaround you can put this at the point you #include them
> in your C++ source.
>
> Exception handling can get tricky when integrating C and C++ code,
> when the stack unwinding has to cross between the languages. Not every
> compiler/library/toolchain combo handles this. You may have to disable
> exception handling (i.e. -fno-exceptions etc). The GNU docs have a
> good overview here:
>
> https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_exceptions.html
>
> —
> Reply to this email directly, view it on GitHub, or unsubscribe.
> You are receiving this because you were mentioned.
|
Beta Was this translation helpful? Give feedback.
0 replies
-
Our last project with cFS involved utilizing a c++ fault management library. Other than a minor annoyance of writing a stub class to manage calls into the library, it was a painless experience and everything worked well. I certainly would encourage more c++ dev with cFS.
-Maurice
From: Mathew Benson ***@***.***>
Sent: Friday, March 24, 2023 10:27 AM
To: nasa/cFS ***@***.***>
Cc: Subscribed ***@***.***>
Subject: Re: [nasa/cFS] Guidelines for implementing apps in C++ (Discussion #659)
Lastly, don’t be afraid of C++ code in CFS. When used effectively, the benefits can outweigh the problems. Stick to certain guidelines and you’ll be fine.
Sent from my iPhone
On Mar 24, 2023, at 12:24, ***@***.*** wrote:
All well said. I can add one more caveat since writing these. Global and static constructors and destructors. This is an issue with C as well but is more noticeable in C++. Again, this has everything to do with late linking. The problem is unloading applications, as eluded to earlier in the chain.
The POSIX and C++ standards are very clear when it comes to loading dynamic libraries and instantiating objects. When a dynamic library is loaded, the dynamic linker loads all ELF section into memory. Unless you do a lazy load, the linker then attempts to satisfy all symbols with no addresses. If this is a library compiled from C++ it will probably also have a ctor and dtor section. Ctor contain functions that are called to initialize global and static c++ objects. This is where the constructors are called. Dtor contains the global and static destructors. Those that have pieced together a VxWorks toolchain outside of Workbench may have noticed you also have to run the “munger” to populate the ctor and dtor sections or your constructors don’t get called. This behavior is well defined. What is supposed to happen when you unload an object is nebulous and you NEED to understand what happens if you’re writing C++ apps.
The POSIX standard does not require the object actually be removed from memory. The module is just flagged as “unloaded”. Your code and data might still be there. In linux, it usually still is. Anybody that has broken the golden rule of never calling app functions from another app may have noticed it actually still works even when the application being called is “unloaded”. The real problem occurs when we try to RELOAD the same app. Linux is smart and knows the file didn’t really change, so it just flags the module back from unloaded to loaded. The standard is very clear and states that the constructors are to be executed when the module is loaded into memory. Since Linux did not actually reload your module into memory, it will not execute your constructors. All your globals and statics will have the same values and parameters they had when you “unloaded” them.
This also affects C code. Globals and statics are contained in the BSS section, which is required to be zero’d out ON LOAD. When you “reload” your app and the binary didn’t actually change, your BSS will probably contain the same values it did when you unloaded it.
Therefore, my organization has the following coding standards:
1. All global and static variables are explicitly initialized (memset your AppData structures).
2. No classes that are instantiated globally or statically can have explicit constructors or destructors. (Put actual initialization code in some sort of an “init” function. Similarly for your destructor code. Call the “Init” and “Deinit” functions at the appropriate time, and avoid certain patterns. Some patterns prohibit the recommended solution.)
And if you try loading a NEW app image compiled from updated code, on a Linux kernel built for embedded use, things can get really freaky on some platforms.
Sent from my iPhone
>> On Mar 21, 2023, at 14:05, Isaac Rowe ***@***.***> wrote:
>>
>
> There's been some prior discussion of this on the cFS-community mailing list. Unfortunately, I think the archive requires NASA authentication. I'm going to post some quotes from other folks:
>
> ***@***.***/thread/OANSPI46GVMPNNNSB467P5LD23F274GO/:
>
> @mbenson1:
>
> In my experience, base classes and especially templates can be problematic, due to the late linking nature of CFS. If you have base classes or templates shared between applications, then this applies. If not, you won’t have a problem. Base classes between applications will collide. He first application loaded won’t have any issues. When the second application is loaded, the symbols will collide. VxWorks will just error outright. Linux will just use the previously loaded symbol, which may or may not be what is intended. We put the base class code in CFS Libs. Just be very careful if you think any dependent application could have a development life cycle outside of the other applications. Even though applications can physically be compiled and loaded independently, of a shared base class is modified, all the applications that use that base class should also be recompiled even if the code has not changed. You could have strange link errors at runtime otherwise.
>
> Templates are trickier due to how the compiler generates the concrete code from the templates. You can’t put the templates code in a lib. It won’t do any good. Dropping a .h template file in the lib won’t do any good since the compiler will automatically generate the concrete .c files from the template for each of the applications that use it. Each application will have its own copy. If the template types are the same, symbols will collide. You might be able to prevent symbol collisions by making all symbols static. Some compilers, like Green Hills, have modes where the generated concrete .c files from the template can be retained and put into a CFS lib. But this makes the code very toolchain specific. I normally just avoid templates in CFS code unless templates are not shared and I know positively beyond the shadow of a doubt that my code won’t be used by other people or on other projects, which is almost never. This was a big problem for a templated math library. We manually instantiated the templates into concrete code.
>
> Aside from that, just the normal linkage issues. Some minor coding standards mismatches, depending on the toolchain, by easily corrected.
>
> CFS doesn't have any unique issues with C++ name mangling that any other C
> application doesn't have. The unique issues are due to the late linking
> nature of application loading.
>
> If you only have one application that is using the library, then it's not a
> problem since you have no shared symbols between applications to collide
> with. If you have multiple applications but they are all using different
> template classes such that each symbol results in a uniquely mangled name,
> there won't be any collisions and everything will be fine. If you have
> multiple applications with the same template class, but no partial
> templates and you're running Linux, you won't have any problem, though I
> would compile all those applications at the same time, to avoid template
> version mismatch. This is because the linux loader just assumes the new
> symbol is the same as the old symbol, though I can positively confirm that
> it only looks at the symbol name. If the function itself is different,
> you'll get strange things happening. This is also why I say "no partial
> templates". If you have partial templates, I'm not sure.
>
> I've wrestled with this template issue for a long time. There are
> definitely ways to get them to work. Odds are other people have used
> templates in the past and not have had problems. I didn't think of any of
> these problems until I was forced to when I wrote the first object oriented
> CFS applications on the Orion program, and immediately discovered that
> VxWorks loader just flat out rejected it when symbols collided and Linux
> allowed it. I had to dig deep in the bowels of the language, toolchain,
> and the loader to understand where the future pitfalls would be. I know
> there are multiple ways around the various issues, but I haven't found an
> easy to use magic bullet that works in all situations. I would love to
> hear from others that have. Our public repository contains 25 object
> oriented C++ applications, containing about 40,000 lines of code (
> https://github.com/windhoverlabs/airliner). We completely rewrote one
> template based math library to remove the templates. It's at
> https://github.com/WindhoverLabs/airliner/tree/develop/apps/px4lib/fsw/src/m....
> It sucks, but we had multiple applications using it and I needed a solution
> that I knew did not have any holes.
>
> A better solution, if I could find a tool to do it, would be similar to one
> of the modes that the Green Hills compiler is capable of building
> templates. Basically all any of the compilers do is use the template .h
> files literally as a template to automatically generate a .cpp file first,
> then it compiles it. Usually it either deletes the generated .cpp file or
> the more modern tools don't even generate the .cpp file on the file
> system. It all happens internally. Green Hills has a mode where it will
> generate the .cpp file, but keep it there after the build is done. There
> may be a way to make gnu tools do that. I just don't know how. But if you
> could just keep the .cpp files that the compiler generates and either
> convert the template .h to a concrete .h or tell the compiler to not
> generate and compile the concrete .cpp in your applications, you could then
> just collect them together into a single CFE library. Then you could
> maintain your templated code independently from the concrete applications.
> Partial templates might still be an issue. I'm not entirely certain how
> those are generated by the precompiler or linked at run time.
>
> Again, none of this is really a CFS unique thing. Anything that supports
> late linking is susceptible.
>
> @iamthad:
>
> Linkage and dynamic loading issues seem to be the majority of problems
> that people have seen with C++ apps in CFS.
>
> As well as the issues Mathew discusses above, note that if you:
>
> wish to be able to unload your app
> are using an OSAL that implements modules using dlopen and dlclose
> (e.g. POSIX)
> are using the GNU C++ toolchain
> then you should be aware of the STB_GNU_UNIQUE symbol binding type
> that is used to ensure certain requirements in the C++ language such
> as "vague linkage".
>
> If your app contains any symbols with this binding, it will not be
> unloaded by dlclose.
>
> More information:
>
> The rationale for adding the feature:
> https://gcc.gnu.org/legacy-ml/gcc-patches/2009-07/msg01240.html
> Some bug reports and discussion:
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66830
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71971
> https://bugzilla.redhat.com/show_bug.cgi?id=1215452
> Allen Brown, Odyssey Space Research:
>
> In addition to what Matt and Thad have said, I had success when placing an
> entire c++ cFS app in its own namespace to protect against symbol
> collisions. Only the C entry point wasn't mangled.
>
> @mbenson1:
>
> And the task delete callback, if used. Those are the only two symbols that
> should ever be called up from the framework to the application.
>
> AB:
>
> That's right. Thanks. Some apps won't need that callback, especially with
> proper C++ RAII on classes you control. But anything is possible when
> dealing with external libraries and/or system resources.
> What I haven't done with C++ cFS apps is attempt to use the existing cFS
> apps that perform a symbol lookup, like MM, MD, and the CFS_LIB. With the
> C++ name mangling and the OSAL symbol length limit these features may have
> problems. You should review your dev and flight troubleshooting use cases.
>
> ***@***.***/thread/3BRMUGGE2TOLGKIP6NIQNZV4UWTJIUAF/:
>
> Chuck Claunch, NASA JSC/Jacobs technology:
>
> We (an autonomy project for Gateway out of JSC/ER4) have successfully used several C++ libraries in our CFS apps. Our approach was to use a C wrapper as an interface to our C++ objects. The C++ objects do all the work and the C wrapper just does the CFS I/O and scheduling. The only hiccup was the annoyance of having to build all the dependencies as 32-bit.
>
> (editor's note: the 32-bit restriction is no longer relevant)
>
> @mbenson1:
>
> I've used C++ libraries and written CFS apps in C++ many times. With one
> exception, there isn't anything different using C++ libraries in CFS than
> using C++ in any C application. The usual C++ to C linkage issues still
> apply so just follow best practices.
>
> The only exception has nothing to do with C vs. C++, but has everything to
> do with libraries and late linking apps. You can get unexpected side
> effects if you link the same library in multiple applications. The dynamic
> loader will have multiple identically named symbols to load and link in.
> The first one won't have any problems, but the second application that is
> loaded in with the same library will either conflict with the already
> loaded symbol and abort the load (VxWorks), or assume its the same symbol
> and link the one that's already loaded (Linux). To avoid it when multiple
> applications require the library, either link the library in the core
> binary or to a CFE Library (CFE_LIB) and load the library before your
> application is loaded.
>
> @jphickey :
>
> A few known caveats:
>
> The CFE header files do not currently include a conditional 'extern
> "C"' block around the declarations. This is required for proper linkage
> to C++. As a workaround you can put this at the point you #include them
> in your C++ source.
>
> Exception handling can get tricky when integrating C and C++ code,
> when the stack unwinding has to cross between the languages. Not every
> compiler/library/toolchain combo handles this. You may have to disable
> exception handling (i.e. -fno-exceptions etc). The GNU docs have a
> good overview here:
>
> https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_exceptions.html
>
> —
> Reply to this email directly, view it on GitHub, or unsubscribe.
> You are receiving this because you were mentioned.
—
Reply to this email directly, view it on GitHub<#659 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AGF3NFXIRZS4TAO456GUBIDW5XKODANCNFSM6AAAAAAVZCX5MI>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Hi all,
Just curious if anyone in the community has experience writing CFS apps in C++. If so, do you have any lessons-learned from the choice of language?
Beyond the various C++ coding standards that are out there, do you have any specific recommendations with respect to the use of CFS APIs in C++?
Thanks!
Beta Was this translation helpful? Give feedback.
All reactions