TOP

Classes and Methods

Applies to: ASN.1/C++ 6.5

General Design

The OSS ASN.1/C++ API consists of control, coding, representation, memory management, and utility classes. Both coding and representation classes are generated from the input ASN.1 specification. Representation classes are used to store actual PDU data in decoded form. Coding classes act as containers for representation classes and can encode and decode. Separating encoding and decoding from the representation results in generated code that is much smaller. Control classes are used to hold the encoded data and to control the encoding or decoding process. Use memory management classes to create non-standard memory allocation and deallocation strategies for representation classes. Also, the ASN.1/C++ Runtime can use these classes for temporary storage. Utility classes are intermediate classes that offer the most convenient value handling for some representation classes.

The API does not use the C++ Standard Template Library and does not depend on it in any way. This makes it possible to use the same OSS ASN.1 Tools for C++ port for different STL implementations.

Error Handling

The OSS ASN.1 Tools for C++ uses exceptions to signal errors, by default.

If your C++ compiler does not provide adequate exception support or exceptions must be avoided, set the legacy "no-exception" error handling function or provide your own error-handling function by calling

void asn1_set_error_handling(void (*func)(int), int complete = FALSE);

Now the ASN.1/C++ runtime will call the specified function whenever an error that cannot be returned as a function result occurs. The error handling function parameter is a non-zero error code. Use the optional second parameter to call the error handling function for every error or only when the error cannot return an error code.

Use the following function to get a textual description of the numeric return code from any ASN.1/C++ API method or constructor:

char *asn1_describe_error_code(int retcode);

The function returns a pointer to the string that contains a description of the given return code, as specified by the retcode parameter. If the return code is invalid or unknown, NULL is returned.

Default "Full-Exception" Error Handling Mode

In modern C++, the preferred way to perform error handling is to use exceptions. Use "full-exception" mode to avoid checking ASN.1/C++ Runtime function results and completely rely on exceptions for error handling. However, if your C++ compiler is not good at exception support you cannot use this mode. The ASN.1/C++ Runtime uses the following default exception class:

class ASN1RuntimeException {
private:
      int code;
public:
      ASN1RuntimeException(int asn1_code);
      ASN1RuntimeException(const ASN1RuntimeException & that);
      int get_code() const;
      char* describe_error() const;
};

Alternatively, you can define a special ASN.1 exception class, use any other exception class, or simply throw error codes as integers, as shown in the following example:

class ASN1Error: public exception {
public:
      ASN1Error(int code);
      . . .
};

Next, the error-handling function, which throws an ASN.1 exception class object, is implemented:

void throw_error(int code)
{
    throw ASN1Error(code);
}

Finally, the ASN.1/C++ Runtime is notified of the new error-handling function:

asn1_set_error_handling(throw_error, TRUE);

TRUE, the second parameter, specifies that the error handling function is called for every error.

Legacy "No-Exception" Error Handling Mode

To maintain compatibility with user code written for the ASN.1/C++ Runtime versions 6.0 and older, set the legacy error handling mode before initializing any ASN.1/C++ objects:

asn1_set_error_handling(asn1_legacy_error_handler);

The pre-defined asn1_legacy_error_handler() function saves the last error code in a static variable. In this mode, an ASN.1/C++ Runtime function usually returns its completion status as the result, though this is not always possible. For example, C++ constructors do not return results, so errors occurring in constructors must be signaled some other way. In legacy error handling mode, read the last error code using the following function:

int asn1_get_last_error();

So, if you are unsure that a constructor will succeed, check it in the following manner:

SomeType a(…);
if (error_code = asn1_get_last_error())
   // an error occurred

To have your application continue after an error has been handled, reset the last error code to 0 using the following function:

void asn1_clean_error();

Remarks

This error handling method is not thread-safe. Since all threads share the last_error_code variable, one thread could disrupt the operations of another. If your application is multithreaded, we recommend using the default "full-exception" error handling or implementing your own error handling function, probably using C++ exceptions.

There is another potential, platform-dependent problem using the legacy error handling mode with some C++ compilers. Memory allocation for representation types is performed using the C++ compilers' operator new functions. These functions call the error handling function when there is insufficient memory. If the error handling function does not throw exceptions, the operator new function returns NULL. Some C++ compilers (e.g., Sun CC, Microsoft VC++) can handle this as a memory allocation failure and avoid calling the constructor, while others (e.g., g++) call the constructor with this pointer equal to NULL, leading to a segmentation fault. So, if you are using g++, your application will not be robust if the legacy error handling mode is used and a memory deficit occurs. Use the "full-exception" or "compatibility" mode instead.

"Compatibility" Error Handling Mode

This mode is similar to the "full-exception" mode but the asn1_set_error_handling function is called with the second argument set to FALSE:

asn1_set_error_handling(throw_error, FALSE);

Or, since FALSE is the default value of the second argument, you can simply omit it:

asn1_set_error_handling(throw_error);

In this mode, the ASN.1/C++ Runtime functions return error codes as results whenever possible, as in the legacy "no-exception" mode. The error handling function is called only when the error code cannot be returned as the function's result, such as in constructors.

"Custom No-Exceptions" Error Handling Mode

You can implement your own error handling function without using C++ exceptions, but this only makes sense in rare situations. For example, your C++ compiler does not support exceptions, or does but consumes excessive resources. Your application is multithreaded, so the legacy error handling mode is unsatisfactory. This is when implementing a custom error handling function without using C++ exceptions is called for.

Several different approaches are possible:

  • When any error causes thread termination, all error handling can be done by the error handling function. This function
    1. Reports the error to the user or to the rest of the application, if necessary.
    2. Does the necessary cleanup, including the destruction of objects allocated by the thread.
    3. Terminates the thread.
  • When the number of threads in the application has an upper limit, employ an approach similar to the legacy error handling mode, but use a static array of error codes (one array element per thread) instead of a single static error code variable. Each thread is assigned an array element, whose number or address is stored where the thread can access it, such as in thread-local storage. The error handling function stores the error code in the current thread's array element. The error checking function, which replaces asn1_get_last_error, returns the current thread's array element. The cleanup function, which replaces asn1_clean_error, assigns 0 to the array element. Since each thread accesses only its own array element, synchronization is unnecessary.
  • When the number of threads is very dynamic with no upper limit, employ a dynamic structure, such as a linked list or hash table, to store the relation between each thread and its current error code. The error handling, error checking, and cleanup functions access this structure. This access must be synchronized, because different threads could attempt to access the structure at the same time.

Remarks

When custom error handing is implemented, the default error handling no longer works. Do not bother calling asn1_get_last_error: it will always return the last value stored in the static error variable when asn1_set_error_handling was called, which is usually 0. Error handling is done exclusively by the custom error handling function. To check the error code, implement a custom error checking function that retrieves the error code from where the error handling function stored it.

The code generated by the ASN.1/C++ Compiler could rely on exception support for the target C++ compiler. If your C++ compiler supports exceptions but you plan to disable this using the appropriate command line option, be sure to define the EXCEPTIONS_SUPPORTED preprocessor symbol as 0 when compiling the generated code. For example, specify –DEXCEPTIONS_SUPPORTED=0 on the C++ compiler command line.

Classes Index

The topics listed below are grouped by class category. Get detailed information about a category, class, or topic by clicking on its name.


This documentation applies to release 6.5 and later of the OSS® ASN.1 Tools for C++.

Copyright © 2017 OSS Nokalva, Inc. All rights reserved.
No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form or by any means electronic, mechanical, photocopying, recording or otherwise, without the prior permission of OSS Nokalva, Inc.
Every distributed copy of the OSS® ASN.1 Tools for C++ is associated with a specific license and related unique license number. That license determines, among other things, what functions of the OSS ASN.1 Tools for C++ are available to you.