|
The purposes of an
API_BEGIN and
API_END block are to:
|
|
|
1.
|
Protect the contained logic block with ACIS exception handling.
|
|
|
2.
|
Provide rollback behavior in the event a modeling operation fails.
|
|
|
The
API_BEGIN macro declares a variable "result" of class "outcome". When performing modeling operations within an
API_BEGIN and
API_END block, the result variable should be used to contain the success or failure of those operations. Take for example the following fictional customer function, which returns true or false if the following set of operations succeeds/fails:
|
|
|
bool do_something(BODY *& retbody)
|
|
{
|
|
bool ret;
|
|
BODY *prism = NULL;
|
|
BODY *cone = NULL;
|
|
|
API_BEGIN
|
|
|
result = api_make_prism(..., prism);
|
|
|
if(result.ok())
|
|
|
result = api_make_frustum(..., cone);
|
|
|
// prism is tool, cone is blank. The result of
|
|
// this call is the blank (cone); the tool (prism)
|
|
// is deleted.
|
|
if(result.ok())
|
|
|
result = api_intersect(prism, cone);
|
|
|
// If a NULL body is returned, then there was no
|
|
// overlap - we consider this failure. Force a rollback
|
|
// by setting "result" to a failing value.
|
|
if (cone == NULL)
|
|
|
result = outcome(API_FAILED);
|
|
|
API_END
|
|
|
// Setup our return values.
|
|
ret = result.ok();
|
|
retbody = cone;
|
|
|
return ret;
|
|
}
|
|
|
There are some significant points to note about this simple example:
|
|
|
The variable result is used for determining the success or failure of the entire
API_BEGIN/API_END logic block. It is even set to
API_FAILED if the prism and cone don't intersect. By doing this, the
API_END will examine result and, if it is non-zero, then rolls back all changes (assuming this is the outermost
API_END).
|
|
In the event of any type of failure, no cleanup is needed. The rollback will perform all cleanup.
|
|
There are no return's or goto's inside the
API_BEGIN/API_END block. This is important because once an
API_BEGIN block is entered, it must be exited through it's corresponding
API_END block.
|
|
Let us take this example one step further by considering how this routine is called:
|
|
|
void main(void)
|
|
{
|
|
API_BEGIN
|
|
|
BODY *bod;
|
|
bool r = do_something(bod);
|
|
|
result = (r == true) : outcome(0) ? outcome(API_FAILED);
|
|
|
// If the result is not ok, no cleanup is needed; all
|
|
// results will be rolled back when we pass through API_END.
|
|
if (result.ok())
|
|
|
api_del_entity(bod);
|
|
|
API_END
|
|
}
|
|
|
This illustrates that nesting of
API_BEGIN/API_END blocks is perfectly acceptable. However, there are important behavioral differences:
|
|
|
If any failures occur within
do_something, it's
API_END does NOT rollback the changes. The rollback always occurs at the outermost
API_END, which is now the
API_END in main.
|
|
It is important for
do_something to propagate the knowledge of success or failure using it's bool return value. It allows main() to realize success or failure and set it's result appropriately. The knowledge of success or failure must be propagated upward through
API_BEGIN/API_END blocks to ensure that the rollback occurs at the outermost
API_END.
|