mirror of
https://github.com/nasa/fpp.git
synced 2025-12-11 19:23:13 -06:00
565 lines
21 KiB
Plaintext
565 lines
21 KiB
Plaintext
== Writing C Plus Plus Implementations
|
|
|
|
When constructing an F Prime deployment in {cpp}, there are generally
|
|
five kinds of implementations you have to write:
|
|
|
|
. Implementations of
|
|
<<Defining-Types_Abstract-Type-Definitions,abstract types>>.
|
|
These are types that are named in the FPP model but are defined
|
|
directly in {cpp}.
|
|
|
|
. Implementations of <<Defining-State-Machines_Writing-a-State-Machine-Definition,
|
|
external state machines>>.
|
|
|
|
. Implementations of
|
|
<<Defining-Components,components>>.
|
|
|
|
. Implementations of any libraries used by the component implementations,
|
|
e.g., algorithm libraries or hardware device driver libraries.
|
|
|
|
. A top-level implementation including a `main` function for running
|
|
the FSW application.
|
|
|
|
Implementing a component (item 3) involves filling out the API provided by
|
|
the {cpp} component base class.
|
|
This process is covered in detail in the F Prime user's guide;
|
|
we won't cover it further here.
|
|
Similarly, implementing libraries (item 4) is unrelated to FPP, so we
|
|
won't cover it in this manual.
|
|
Here we focus on items 1, 2, and 5: implementing abstract types,
|
|
implementing external state machines, and implementing deployments.
|
|
We also discuss *serialization* of data values, i.e., representing
|
|
FPP data values as binary data for storage and transmission.
|
|
|
|
=== Implementing Abstract Types
|
|
|
|
When translating to {cpp}, an
|
|
<<Defining-Types_Abstract-Type-Definitions,abstract type definition>>
|
|
represents a {cpp} class that you write directly in {cpp}.
|
|
When you use an abstract type _T_ in an FPP definition _D_ (for example, as the
|
|
member type of an array definition)
|
|
and you translate _D_ to {cpp}, then the generated {cpp} for _D_ contains an
|
|
`include` directive that includes a header file for _T_.
|
|
|
|
As an example, try this:
|
|
|
|
----
|
|
% fpp-to-cpp -p $PWD
|
|
type T
|
|
array A = [3] T
|
|
^D
|
|
----
|
|
|
|
Notice that we used the option `-p $PWD`.
|
|
This is to make the generated include path relative to the current directory.
|
|
|
|
Now run
|
|
|
|
----
|
|
% cat AArrayAc.hpp
|
|
----
|
|
|
|
You should see the following line in the generated {cpp}:
|
|
|
|
[source,cpp]
|
|
----
|
|
#include "T.hpp"
|
|
----
|
|
|
|
This line says that in order to compile `AArrayAc.cpp`,
|
|
a header file `T.hpp` must exist in the current directory.
|
|
It is up to you to provide that header file.
|
|
|
|
*General implementations:*
|
|
In most cases, when implementing an abstract type `T` in {cpp}, you
|
|
will define
|
|
a class that extends `Fw::Serializable` from the F Prime framework.
|
|
Your class definition should include the following:
|
|
|
|
* An implementation of the virtual function
|
|
+
|
|
----
|
|
Fw::SerializeStatus T::serializeTo(Fw::SerializeBufferBase&, Fw::Serialization::Endianness = Fw::Serialization::BIG) const
|
|
----
|
|
+
|
|
that specifies how to *serialize* a class instance to a buffer
|
|
(i.e., convert a class instance to a byte string stored in a buffer).
|
|
|
|
* An implementation of the function
|
|
+
|
|
----
|
|
Fw::SerializeStatus T::deserializeFrom(Fw::SerializeBufferBase&, Fw::Serialization::Endianness = Fw::Serialization::BIG)
|
|
----
|
|
+
|
|
that specifies how to *deserialize* a class instance from a
|
|
buffer (i.e., reconstruct a class instance from a byte string stored in a
|
|
buffer).
|
|
|
|
* A constant `T::SERIALIZED_SIZE` that specifies the size in bytes
|
|
of a byte string serialized from the class.
|
|
|
|
* A zero-argument constructor `T()`.
|
|
|
|
* An overloaded equality operator
|
|
+
|
|
----
|
|
bool operator==(const T& that) const;
|
|
----
|
|
|
|
For more on serialization, see the section on
|
|
<<Writing-C-Plus-Plus-Implementations_Serialization-of-FPP-Values,
|
|
serialization of FPP values>>.
|
|
|
|
Here is a minimal complete implementation of an abstract type `T`.
|
|
It has one member variable `x` of type `U32` and no methods other than
|
|
those required by F Prime.
|
|
We have made `T` a {cpp} struct (rather than a class) so that
|
|
all members are public by default.
|
|
To implement `serializeTo`, we use the `serializeFrom` function
|
|
provided by `Fw::SerializeBufferBase`.
|
|
|
|
----
|
|
// A minimal implementation of abstract type T
|
|
|
|
#ifndef T_HPP
|
|
#define T_HPP
|
|
|
|
// Include Fw/Types/Serializable.hpp from the F Prime framework
|
|
#include "Fw/Types/Serializable.hpp"
|
|
|
|
struct T final : public Fw::Serializable { // Extend Fw::Serializable
|
|
|
|
// Define some shorthand for F Prime types
|
|
using SS = Fw::SerializeStatus SS;
|
|
using B = Fw::SerializeBufferBase B;
|
|
using E = Fw::Serialization::Endianness E;
|
|
|
|
// Define the constant SERIALIZED_SIZE
|
|
enum Constants { SERIALIZED_SIZE = sizeof(U32) };
|
|
|
|
// Provide a zero-argument constructor
|
|
T() : x(0) { }
|
|
|
|
// Define a comparison operator
|
|
bool operator==(const T& that) const { return this->x == that.x; }
|
|
|
|
// Define the virtual serializeTo method
|
|
SS serializeTo(B& b, E e) const final { return b.serializeFrom(x, e); }
|
|
|
|
// Define the virtual deserializeFrom method
|
|
SS deserializeFrom(B& b, E e) final { return b.deserializeTo(x, e); }
|
|
|
|
// Provide some data
|
|
U32 x;
|
|
|
|
};
|
|
|
|
#endif
|
|
----
|
|
|
|
*Serializable buffers used in ports:*
|
|
In some cases, you may want to define an abstract type `T` that represents
|
|
a data buffer and that is used only in <<Defining-Ports,port definitions>>.
|
|
In this case you can implement
|
|
`T` as a class that extends `Fw::SerializeBufferBase`.
|
|
Instead of implementing the `serializeTo` and `deserializeFrom` functions
|
|
directly, you override functions that get the address and the capacity
|
|
(allocated size) of the buffer; the base class `Fw::SerializeBufferBase`
|
|
uses these functions to implement `serializeTo` and `deserializeFrom`.
|
|
For an example of how to do this, see the files `Fw/Cmd/CmdArgBuffer.hpp`
|
|
and `Fw/Cmd/CmdArgBuffer.cpp` in the F Prime repository.
|
|
Be careful, though: if you implement an abstract type `T` this way
|
|
and you try to use the type `T` outside of a port definition,
|
|
the generated {cpp} may not compile.
|
|
|
|
=== Implementing External State Machines
|
|
|
|
An <<Defining-State-Machines_Writing-a-State-Machine-Definition,
|
|
external state machine>> refers to a state machine implementation
|
|
supplied outside the FPP model.
|
|
To implement an external state machine, you can use
|
|
the https://github.com/JPLOpenSource/STARS/tree/main[State Autocoding for
|
|
Real-Time Systems (STARS)]
|
|
tool.
|
|
STARS provides several ways to specify state machines, and it
|
|
provides several {cpp} back ends.
|
|
The F Prime back end is designed to work with FPP code generation.
|
|
|
|
For an example of an external state machine implemented in STARS,
|
|
see `FppTest/state_machine` in the F Prime repository.
|
|
|
|
=== Implementing Deployments
|
|
|
|
At the highest level of an F Prime implementation, you write
|
|
two units of {cpp} code:
|
|
|
|
. Application-specific definitions visible
|
|
both to the `main` function and to the auto-generated
|
|
topology code.
|
|
|
|
. The `main` function.
|
|
|
|
We describe each of these code units below.
|
|
|
|
==== Application-Specific Definitions
|
|
|
|
As discussed in the section on
|
|
<<Analyzing-and-Translating-Models_Generating-C-Plus-Plus_Topology-Definitions,
|
|
generating {cpp} topology definitions>>, when you translate an FPP
|
|
topology _T_ to {cpp}, the result goes into files
|
|
_T_ `TopologyAc.hpp` and _T_ `TopologyAc.cpp`.
|
|
The generated file _T_ `TopologyAc.hpp` includes a file
|
|
_T_ `TopologyDefs.hpp`.
|
|
The purpose of this file inclusion is as follows:
|
|
|
|
. _T_ `TopologyDefs.hpp` is not auto-generated.
|
|
You must write it by hand as part of your {cpp} implementation.
|
|
|
|
. Because _T_ `TopologyAc.cpp` includes _T_ `TopologyAc.hpp`
|
|
and _T_ `TopologyAc.hpp` includes _T_ `TopologyDefs.hpp`,
|
|
the handwritten definitions in _T_ `TopologyDefs.hpp` are visible
|
|
to the auto-generated code in _T_ `TopologyAc.hpp` and
|
|
`TopologyAc.cpp`.
|
|
|
|
. You can also include _T_ `TopologyDefs.hpp` in your main
|
|
function (described in the next section) to make its
|
|
definitions visible there.
|
|
That way `main` and the auto-generated topology
|
|
code can share these custom definitions.
|
|
|
|
_T_ `TopologyDefs.hpp`
|
|
must be located in the same directory where the topology _T_ is defined.
|
|
When writing the file _T_ `TopologyDefs.hpp`, you should
|
|
follow the description given below.
|
|
|
|
*Topology state:*
|
|
_T_ `TopologyDefs.hpp` must define a type
|
|
`TopologyState` in the {cpp} namespace
|
|
corresponding to the FPP module where the topology _T_ is defined.
|
|
For example, in `SystemReference/Top/topology.fpp` in the
|
|
https://github.com/fprime-community/fprime-system-reference/blob/main/SystemReference/Top/topology.fpp[F Prime system reference deployment], the FPP topology `SystemReference` is defined in the FPP
|
|
module `SystemReference`, and so in
|
|
https://github.com/fprime-community/fprime-system-reference/blob/main/SystemReference/Top/SystemReferenceTopologyDefs.hpp[`SystemReference/Top/SystemReferenceTopologyDefs.hpp`], the type `TopologyState`
|
|
is defined in the namespace `SystemReference`.
|
|
|
|
`TopologyState` may be any type.
|
|
Usually it is a struct or class.
|
|
The {cpp} code generated by FPP passes a value `state` of type `TopologyState` into
|
|
each of the functions for setting up and tearing down topologies.
|
|
You can read this value in the code associated with your
|
|
<<Defining-Component-Instances_Init-Specifiers,
|
|
init specifiers>>.
|
|
|
|
In the F Prime system reference example, `TopologyState`
|
|
is a struct with two member variables: a C-style string
|
|
`hostName` that stores a host name and a `U32` value `portNumber`
|
|
that stores a port number.
|
|
The main function defined in `Main.cpp` parses the command-line
|
|
arguments to the application, uses the result to create an object
|
|
`state` of type `TopologyState`, and passes the `state` object
|
|
into the functions for setting up and tearing down the topology.
|
|
The `startTasks` phase for the `comDriver` instance uses the `state`
|
|
object in the following way:
|
|
|
|
[source,fpp]
|
|
--------
|
|
phase Fpp.ToCpp.Phases.startTasks """
|
|
// Initialize socket server if and only if there is a valid specification
|
|
if (state.hostName != nullptr && state.portNumber != 0) {
|
|
Os::TaskString name("ReceiveTask");
|
|
// Uplink is configured for receive so a socket task is started
|
|
comDriver.configure(state.hostName, state.portNumber);
|
|
comDriver.startSocketTask(
|
|
name,
|
|
true,
|
|
ConfigConstants::SystemReference_comDriver::PRIORITY,
|
|
ConfigConstants::SystemReference_comDriver::STACK_SIZE
|
|
);
|
|
}
|
|
"""
|
|
--------
|
|
|
|
In this code snippet, the expressions `state.hostName` and `state.portNumber`
|
|
refer to the `hostName` and `portNumber` member variables of the
|
|
state object passed in from the main function.
|
|
|
|
The `state` object is passed in to the setup and teardown functions
|
|
via `const` reference.
|
|
Therefore, you may read, but not write, the `state` object in the
|
|
code associated with the init specifiers.
|
|
|
|
*Health ping entries:*
|
|
If your topology uses an instance of the standard component `Svc::Health` for
|
|
monitoring
|
|
the health of components with threads, then _T_ `TopologyDefs.hpp`
|
|
must define the *health ping entries* used by the health component instance.
|
|
The health ping entries specify the time in seconds to wait for the
|
|
receipt of a health ping before declaring a timeout.
|
|
For each component being monitored, there are two timeout intervals:
|
|
a warning interval and a fatal interval.
|
|
If the warning interval passes without a health ping, then a warning event occurs.
|
|
If the fatal interval passes without a health ping, then a fatal event occurs.
|
|
|
|
You must specify the health ping entries in the namespace corresponding
|
|
to the FPP module where _T_ is defined.
|
|
To specify the health ping entries, you do the following:
|
|
|
|
. Open a namespace `PingEntries`.
|
|
|
|
. In that namespace, open a namespace corresponding to the name
|
|
of each component instance with health ping ports.
|
|
|
|
. Inside namespace in item 2, define a {cpp} enumeration with
|
|
the following constants `WARN` and `FATAL`.
|
|
Set `WARN` equal to the warning interval for the enclosing
|
|
component instance.
|
|
Set `FATAL` equal to the fatal interval.
|
|
|
|
For example, here are the health ping entries from
|
|
`SystemReference/Top/SystemReferenceTopologyDefs.hpp`
|
|
in the F Prime system reference repository:
|
|
|
|
[source,cpp]
|
|
----
|
|
namespace SystemReference {
|
|
|
|
...
|
|
|
|
// Health ping entries
|
|
namespace PingEntries {
|
|
namespace SystemReference_blockDrv { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_chanTlm { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_cmdDisp { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_cmdSeq { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_eventLogger { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_fileDownlink { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_fileManager { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_fileUplink { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_imageProcessor { enum {WARN = 3, FATAL = 5}; }
|
|
namespace SystemReference_prmDb { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_processedImageBufferLogger { enum {WARN = 3, FATAL = 5}; }
|
|
namespace SystemReference_rateGroup1Comp { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_rateGroup2Comp { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_rateGroup3Comp { enum { WARN = 3, FATAL = 5 }; }
|
|
namespace SystemReference_saveImageBufferLogger { enum { WARN = 3, FATAL = 5 }; }
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
*Other definitions:*
|
|
You can put any compile-time definitions you wish into _T_ `TopologyAc.hpp`
|
|
If you need link-time definitions (e.g., to declare variables with storage),
|
|
you can put them in _T_ `TopologyAc.cpp`, but this is not required.
|
|
|
|
For example, `SystemReference/Top/SystemReferenceTopologyAc.hpp` declares
|
|
a variable `SystemReference::Allocation::mallocator` of type `Fw::MallocAllocator`.
|
|
It provides an allocator used in the setup and teardown
|
|
of several component instances.
|
|
The corresponding link-time symbol is defined in `SystemReferenceTopologyDefs.cpp`.
|
|
|
|
==== The Main Function
|
|
|
|
You must write a main function that performs application-specific
|
|
and system-specific tasks such as parsing command-line arguments,
|
|
handling signals, and returning a numeric code to the system on exit.
|
|
Your main code can use the following public interface provided
|
|
by _T_ `TopologyAc.hpp`:
|
|
|
|
[source,cpp]
|
|
----
|
|
// ----------------------------------------------------------------------
|
|
// Public interface functions
|
|
// ----------------------------------------------------------------------
|
|
|
|
//! Set up the topology
|
|
void setup(
|
|
const TopologyState& state //!< The topology state
|
|
);
|
|
|
|
//! Tear down the topology
|
|
void teardown(
|
|
const TopologyState& state //!< The topology state
|
|
);
|
|
----
|
|
|
|
These functions reside in the {cpp} namespace corresponding to
|
|
the FPP module where the topology _T_ is defined.
|
|
|
|
On Linux, a typical main function might work this way:
|
|
|
|
. Parse command-line arguments. Use the result to construct
|
|
a `TopologyState` object `state`.
|
|
|
|
. Set up a signal handler to catch signals.
|
|
|
|
. Call _T_ `::setup`, passing in the `state` object, to
|
|
construct and initialize the topology.
|
|
|
|
. Start the topology running, e.g., by looping in the main thread
|
|
until a signal is handled, or by calling a start function on a
|
|
timer component (see, e.g., `Svc::LinuxTimer`).
|
|
The loop or timer typically runs until a signal is caught, e.g.,
|
|
when the user presses control-C at the console.
|
|
|
|
. On catching a signal
|
|
|
|
.. Set a flag that causes the main loop to exit or the timer
|
|
to stop.
|
|
This flag must be a volatile and atomic variable (e.g.,
|
|
`std::atomic_bool`) because it is accessed
|
|
concurrently by signal handlers and threads.
|
|
|
|
.. Call _T_ `::teardown`, passing in the `state` object, to
|
|
tear down the topology.
|
|
|
|
.. Wait some time for all the threads to exit.
|
|
|
|
.. Exit the main thread.
|
|
|
|
For an example like this, see `SystemReference/Top/Main.cpp` in the
|
|
F Prime system reference repository.
|
|
|
|
==== Public Symbols
|
|
|
|
The header file _T_ `TopologyAc.hpp` declares several public
|
|
symbols that you can use when writing your main function.
|
|
|
|
*Instance variables:*
|
|
Each component instance used in the topology is declared as
|
|
an `extern` variable, so you can refer to any component instance
|
|
in the main function.
|
|
For example, the main function in the `SystemReference` topology
|
|
calls the method `callIsr` of the `blockDrv` (block driver)
|
|
component instance, in order to simulate an interrupt service
|
|
routine (ISR) call triggered by a hardware interrupt.
|
|
|
|
*Helper functions:*
|
|
The auto-generated `setup` function calls the following auto-generated
|
|
helper functions:
|
|
|
|
[source,cpp]
|
|
----
|
|
void initComponents(const TopologyState& state);
|
|
void configComponents(const TopologyState& state);
|
|
void setBaseIds();
|
|
void connectComponents();
|
|
void regCommands();
|
|
void readParameters();
|
|
void loadParameters();
|
|
void startTasks(const TopologyState& state);
|
|
----
|
|
|
|
The auto-generated `teardown` function calls the following
|
|
auto-generated helper functions:
|
|
|
|
[source,cpp]
|
|
----
|
|
void stopTasks(const TopologyState& state);
|
|
void freeThreads(const TopologyState& state);
|
|
void tearDownComponents(const TopologyState& state);
|
|
----
|
|
|
|
The helper functions are declared as public symbols in _T_
|
|
`TopologyAc.hpp`, so if you wish, you may write your own versions
|
|
of `setup` and `teardown` that call these functions directly.
|
|
The FPP modeling is designed so that you don't have to do this;
|
|
you can put any custom {cpp} code for setup or teardown into
|
|
<<Defining-Component-Instances_Init-Specifiers,init specifiers>>
|
|
and let the FPP translator generate complete `setup` and `teardown`
|
|
functions that you simply call, as described above.
|
|
Using init specifiers generally produces cleaner integration between
|
|
the model and the {cpp} code: you write the custom
|
|
{cpp} code once, any topology _T_ that uses an instance _I_ will pick
|
|
up the custom {cpp} code for _I_, and the FPP translator will automatically
|
|
put the code for _I_ into the correct place in _T_ `TopologyAc.cpp`.
|
|
However, if you wish to write the custom code directly into your main
|
|
function, you may.
|
|
|
|
=== Serialization of FPP Values
|
|
|
|
Every value represented in FPP can be *serialized*, i.e., converted into a
|
|
machine-independent sequence of bytes.
|
|
Serialization provides a consistent way to store data (e.g.,
|
|
to onboard storage) and to transmit data (e.g., to or from the ground).
|
|
The F Prime framework also uses serialization to pass data through asynchronous
|
|
port invocations.
|
|
The data is serialized when it is put on a message queue
|
|
and then *deserialized* (i.e., converted from a byte sequence to
|
|
a {cpp} representation)
|
|
when it is taken off the queue for processing.
|
|
|
|
F Prime uses the following rules for serializing data:
|
|
|
|
. Values of primitive integer type are serialized as follows:
|
|
|
|
.. A value of unsigned integer type (`U8`, `U16`, `U32`, or `U64`)
|
|
is serialized into big-endian order (most significant byte first) by default,
|
|
using the number of bytes implied by the bit width.
|
|
For example, the `U16` value 10 (decimal) is serialized as the
|
|
two bytes `00` `0A` (hex). If little-endian order is desired, the optional mode parameter can be specified as `Fw::Serialization::LITTLE`. This stores the data least significant byte order. The `U16` value 10 (decimal) is serialized in little-endian as the two bytes `0A` `00` (hex).
|
|
|
|
.. A value of signed integer type (`I8`, `I16`, `I32`, or `I64`)
|
|
is serialized by first converting the value to an unsigned value of the same bit
|
|
width and then serializing the unsigned value as stated in rule 1.a.
|
|
If the value is nonnegative, then the unsigned value is
|
|
the same as the signed value.
|
|
Otherwise the unsigned value is the two's complement of the signed value.
|
|
For example:
|
|
|
|
... The `I16` value 10 (decimal) is serialized as two bytes, yielding the bytes `00` `0A` (hex) in big-endian and `0A` `00` (hex) in little-endian.
|
|
|
|
... The `I16` value -10 (decimal) is serialized by
|
|
(1) computing the `U16` value 2^16^ - 10 = 65526
|
|
and (2) serializing that value as two bytes in the selected byte order,
|
|
yielding the bytes `FF` `F6` (hex) big-endian and `F6` `FF` (hex) little-endian.
|
|
|
|
. A value of floating-point type (`F32` or `F64`)
|
|
is serialized in the selected byte order according to the IEEE
|
|
standard for representing these values.
|
|
|
|
. A value of Boolean type is serialized as a single byte.
|
|
The byte values used to represent `true` and `false`
|
|
are `FW_SERIALIZE_TRUE_VALUE` and `FW_SERIALIZE_FALSE_VALUE`,
|
|
which are defined in the F Prime configuration header `FpConfig.h`.
|
|
|
|
. A value of string type is serialized as a size followed
|
|
by the string characters in string order.
|
|
|
|
.. The size is serialized according to rule 1 for primitive
|
|
integer types.
|
|
The F Prime type definition `FwSizeStoreType` specifies the type to use
|
|
for the size.
|
|
This type definition is user-configurable; it is found in the
|
|
F Prime configuration file `FpConfig.fpp`.
|
|
|
|
.. There is one byte for each character of the string, and there is
|
|
no null terminator.
|
|
Each string character is serialized as an `I8` value according to rule 1.b.
|
|
|
|
. A value of <<Defining-Types_Array-Type-Definitions,array type>>
|
|
is serialized as a sequence of serialized values, one for each array
|
|
element, in array order.
|
|
Each value is serialized using these rules.
|
|
|
|
. A value of <<Defining-Types_Struct-Type-Definitions,struct type>>
|
|
is serialized member-by-member, in the order
|
|
that the members appear in the FPP struct definition,
|
|
with no padding.
|
|
|
|
.. Except for
|
|
<<Defining-Types_Struct-Type-Definitions_Member-Arrays,member arrays>>,
|
|
each member is serialized using these rules.
|
|
|
|
.. Each member array is serialized as stated in rule 5.
|
|
|
|
. A value of <<Defining-Enums,enum type>> is converted to a primitive
|
|
integer value of the <<Defining-Enums_The-Representation-Type,representation
|
|
type>> specified in the enum definition.
|
|
This value is serialized as stated in rule 1.
|
|
|
|
. A value of <<Defining-Types_Abstract-Type-Definitions,abstract type>> is
|
|
serialized according to its
|
|
<<Writing-C-Plus-Plus-Implementations_Implementing-Abstract-Types,
|
|
{cpp} implementation>>.
|