5 Applying General Coding Guidelines

This chapter contains the following topics:

5.1 Using Function Calls

Reuse of existing functions through a function call prevents duplicate code. Refer to these guidelines when using function calls:

  • Always put a comma between each parameter. Optionally, you can add a space for readability.

  • If the function has a return value, always check the return of the function for errors or a valid value.

  • Use jdeCallObject to call another business function.

  • When calling functions with long parameter lists, the function call should not be wider than 80 characters.

    Break the parameter list into one or more lines, aligning the first parameter of proceeding lines with the first parameter in the parameter list.

  • Make sure the data types of the parameters match the function prototype.

    When intentionally passing variables with data types that do not match the prototype, explicitly cast the parameters to the correct data type.

5.1.1 Calling an External Business Function

Use jdeCallObject to call an external business function defined in the Object Management Workbench. Include the header file for the external business function that contains the prototype and data structure definition. It is good practice to check the value of the return code.

5.1.1.1 Example: Calling an External Business Function

This example calls an external business function:

/*-----------------------------------------------------------
 *
 * Retrieve account master information
 *
 *----------------------------------------------------------*/
   idReturnCode = jdeCallObject(_J("ValidateAccountNumber),
                                NULL,
                                lpBhvrCom,
                                lpVoid,
                                (void*) &dsValidateAccount,
                                (CALLMAP*) NULL,
                                (int) 0,
                                (JCHAR*) NULL,
                                (JCHAR*) NULL,
                                (int) 0 );
   if ( idReturnCode == ER_SUCCESS )
   {
      statement;
   }

5.1.2 Calling an Internal Business Function

You can access internal business functions (internal C functions) within the same source file.

You may create modular subroutines that can be accessed from multiple source files. Use CALLIBF(fcn(parm1,parm2)) andCALLIBFRET(ret,fcn(parm1,parm2)) to access internal business functions within a different source file but within the same DLL. Use CALLIBF to call an internal business function with no return value. Use CALLIBFRET to call an internal business function with a return value. Both CALLIBF and CALLIFBRET can call internal business functions with any type or number of parameters.

CALLIBF and CALLIBFRET can only call internal functions within the same business function DLL. They cannot call functions in other business function DLLs. For example, if the internal function intFcn123() is in B550001.C, which is in the CALLBSFN.DLL, you cannot called it with CALLIBF or CALLIBFRET from a business function in CDIST.DLL.

To use CALLIBF or CALLIBFRET for an internal business function, the business function must have its prototype in the business function header. If you do not want other modules calling the internal business function, place the prototype in the C file, not the header file.

Calling internal business functions has several advantages over external business functions. First, they do not have the jdeCallObject performance overhead of checking OCM mapping and possibly executing the function remotely. A called function always executes in the same process from where it was called. Second, the parameters are not restricted to JD Edwards EnterpriseOne data dictionary data types. Any valid C data type, including pointers, may be passed in and out of internal functions.

5.1.2.1 Example: Calling an Internal Business Function with No Return Value

This example calls an internal business function that has no return value.

This portion is an example of b550001.h:

/* normal business function header pieces */
...
/* The internal business function prototype must be in the header for other
   modules to call it */
   void i550001(int *a, int b);

This portion is an example of b550001.c:

/* normal business function code pieces */
#include <b550001.h>
JDEBFRTN(ID) JDEBFWINAPI TestBSFN(LPBHVRCOM  lpVhvrCom,
                                  LPVOID   lpVoid,
                                  LPDSB550001 lpDS)
{
...
}
void i550001(int *a, int b)
{
   *a = *a + b;
   return;
}

This portion is an example of b550002.c:

/* normal business function code pieces */
#include <b550002.h>
#include <b550001.h>

JDEBFRTN(ID) JDEBFWINAPI TestBSFN(LPBHVRCOM  lpBhvrCom,
                                  LPVOID   lpVoid,
                                  LPDSB550001 lpDS)
{
   int total = 3;
   int adder = 7;

   CALLIBF(i550001(&total,adder));
}

5.1.2.2 Example: Calling an Internal Business Function with a Return Value

This example calls an internal business function that has a return value.

This portion is an example of b550001.h:

/* normal business function header pieces */
...
/* The internal business function prototype must be in the header for 
other modules to call it */

   int i550001(int a, int b);

This portion is an example of b550001.c:

/* normal business function code pieces */
#include <b550001.h>

JDEBFRTN(ID) JDEBFWINAPI TestBSFN(LPBHVRCOM   lpBhvrCom,
                                  LPVOID      lpVoid,
                                  LPDSB550001 lpDS)
{
...
}
int i550001(int a, int b)
{
   a = a + b;
   return;
}

This portion is an example of b550002.c:

/* normal business function code pieces */
#include <b550002.h>
#include <b550001.h>

JDEBFRTN(ID) JDEBFWINAPI TestBSFN(LPBHVRCOM   lpBhvrCom,
                                  LPVOID      lpVoid,
                                  LPDSB550001 lpDS)
{
   int total  = 0;
   int adder1 = 6;
   int adder2 = 7;
   CALLIBFRET(total,i550001(adder1,adder2));
}

5.2 Passing Pointers between Business Functions

Never pass pointers directly in or out of business functions. A pointer memory address should not be greater than 32 bits. If you pass a pointer address that exceeds 32 bits across the platform to a client that supports just 32 bits, the significant digit might be truncated and invalidate the address.

The correct way to share pointers between business functions is to store the address in an array. This array is located on the server platform specified in the Object Configuration Manager (OCM). The array allows up to 100 memory locations to be allocated and stored, and it is maintained by JD Edwards EnterpriseOne tools. The index to a position in the array is a long integer type or ID. Use the GENLNG data dictionary object in the business function data structure to pass this index in or out of the business function.

5.2.1 Storing an Address in an Array

Use jdeStoreDataPtr to store an allocated memory pointer in an array for later retrieval. The index to the position in the array is returned. This index should be passed out through the business function data structure (lpDS).

5.2.1.1 Example: Storing an Address in an Array

This example illustrates how to store an address in an array:

If (lpDS->cReturnF4301PtrFlag == _J('1'))
{
   lpDS->idF4301RowPtr = jdeStoreDataPtr(hUser,(void *)lpdsF4301);
}

5.2.2 Retrieving an Address from an Array

Use jdeRetrieveDataPtr to retrieve an address outside the current business function. The index to the position in the array should be passed in through the business function data structure (lpDS). When you use jdeRetrieveDataPtr, the address remains in the array and can be retrieved again later.

5.2.2.1 Example: Retrieving an Address from an Array

This example retrieves an address from an array:

 lpdsF43199 = (LPF43199) jdeRetrieveDataPtr
              (hUser, lpDS->idF43199Pointer);

5.2.3 Removing an Address from an Array

Use jdeRemoveDataPtr to remove the address from the array cell and release the array cell. The index to the position in the array should be passed in through the business function data structure (lpDS). A corresponding call to jdeRemoveDataPtr must exist for every jdeStoreDataPtr. If you use jdeAlloc to allocate memory, use jdeFree to free the memory.

5.2.3.1 Example: Removing an Address from an Array

This example removes an address from an array:

if (lpDS->idGenericLong != (ID) 0)
{
   lpGenericPtr = (void *)jdeRemoveDataPtr(hUser,lpDS->idGenericLong);
   if (lpGenericPtr != (void *) NULL)
   {
      jdeFree((void *)lpGenericPtr);
      lpDS->idGenericLong = (ID) 0;
      lpGenericPtr = (void *) NULL;
   }
}

5.3 Allocating and Releasing Memory

Use jdeAlloc to allocate memory. Because jdeAlloc affects performance, use it sparingly.

Use jdeFree to release memory within a business function. For every jdeAlloc, a jdeFree should exist to release the memory.

Note:

Use the business function FreePtrToDataStructure, B4000640, to release memory through event rule logic.

5.3.1 Example: Allocating and Releasing Memory within a Business Function

This example uses jdeAlloc to allocate memory, and then, in the function cleanup section, jdeFree to release memory:

statement
lpdsF4301 = (LPF4301)jdeAlloc( COMMON_POOL,sizeof(F4301),MEM_ZEROINIT ) ;
statement

/**************************************************************
 * Function Clean Up Section
 **************************************************************/
if (lpdsF4301 != (LPF4301) NULL)
{
   jdeFree( lpdsF4301 );
}

5.4 Using hRequest and hUser

Some API calls require either anhUser or anhRequest variable, or both. To get the hUser, use JDBInitBhvr. To get the hRequest, use JDBOpenTable. Initialize hUser and hRequest to NULL in the variable declaration line. All hRequest and hUser declarations should have JDB_CloseTable( ) and JDB_FreeBhvr( ) in the function cleanup section.

5.5 Typecasting

Typecasting is also known as type conversion. Use typecasting when the function requires a certain type of value, when defining function parameters, and when allocating memory with jdeAlloc().

Note:

This standard is for all function calls as well as function prototypes.

5.6 Comparison Testing

Always use explicit tests for comparisons. Do not embed assignments in comparison tests. Assign a value or result to a variable and use the variable in the comparison test.

Always test floating point variables using <= or >=. Do not use == or != since some floating point numbers cannot be represented exactly.

5.6.1 Example: Comparison Test

This example shows how to create C code for comparison tests.

eJDEDBResult = JDB_InitBhvr ((void*)lpBhvrCom,
                             &hUser,
                             (JCHAR *) NULL,
                             JDEDB_COMMIT_AUTO);

/** Check for Valid hUser **/
if (eJDEDBResult == JDEDB_PASSED)
{
   statement;
}

5.6.2 Example: Creating TRUE or FALSE Test Comparison that Uses Boolean Logic

This example is a TRUE or FALSE test comparison that uses Boolean logic:

/* IsStringBlank has a BOOL return type. It will always return either
 * TRUE or FALSE */
if ( IsStringBlank( szString) )
{
   statement;
} 

5.7 Copying Strings with jdeStrcpy or jdeStrncpy

When copying strings of the same length, such as business unit, you may use the jdeStrcpy ANSI API. If the strings differ in length-as with a description-use the jdeStrncpy ANSI API with the number of characters you need returned, not counting the trailing NULL character.

/**********************************************************
 * Variable Definitions
 **********************************************************/
 JCHAR      szToBusinessUnit(13);
 JCHAR      szFromBusinessUnit(13);
 JCHAR      szToDescription(31);
 JCHAR      szFromDescription(41);
/**********************************************************
 * Main Processing
 **********************************************************/
   jdeStrcpy((JCHAR *) szToBusinessUnit,
             (const JCHAR *) szFromBusinessUnit );


   jdeStrncpy((JCHAR *) szToDescription,
              (const JCHAR *) szFromDescription,
              DIM(szToDescription)-1 );

5.8 Using the Function Clean Up Area

Use the function clean up area to release any allocated memory, including hRequest and hUser.

5.8.1 Example: Using the Function Clean Up Area to Release Memory

This example shows how to release memory in the function clean up area:

lpdsF4301 = (LPF4301)jdeAlloc( COMMON_POOL,
                               sizeof(F4301),MEM_ZEROINIT ) ;
/****************************************************************
 * Function Clean Up Section
 ****************************************************************/
if (lpdsF4301 != (LPF4301 ) NULL)
{
   jdeFree( lpdsF4301 );
}

if (hRequestF4301 != (HREQUEST) NULL)
{
   JDB_CloseTable( hRequestF4301 );
}

JDB_FreeBhvr( hUser );

return ( idReturnValue ) ;

5.9 Inserting Function Exit Points

Where possible, use a single exit point (return) from the function. The code is more structured when a business function has a single exit point. The use of a single exit point also enables the programmer to perform cleanup, such as freeing memory and terminating ODBC requests, immediately before the return. In more complex functions, this action might be difficult or unreasonable. Include the necessary cleanup logic, such as freeing memory and terminating ODBC requests, when programming an exit point in the middle of a function.

Use the return value of the function to control statement execution. Business functions can have one of two return values: ER_SUCCESS or ER_ERROR. By initializing the return value for the function to ER_SUCCESS, the return value can be used to determine the processing flow.

5.9.1 Example: Inserting an Exit Point in a Function

This example illustrates the use of a return value for the function to control statement execution:

   ID         idReturn        = ER_SUCCESS;
/**************************************************************
 * Main Processing
 **************************************************************/
   memset( (void *)(&dsInfo), 0x00, sizeof(DSX51013_INFO) );
   idReturn = X51013_VerifyAndRetrieveInformation( lpBhvrCom,
                                                   lpVoid,
                                                   lpDS,
                                                   &dsInfo );
 /** Check for Errors and Company or Job Level Projections **/
 if ( (idReturn == ER_SUCCESS) &&
      (lpDS->cJobCostProjections == _J('Y')) )
 {
    /** Process All Periods between the From and Thru Dates **/
    while ( (!dsInfo.bProcessed) &&
            (idReturn == ER_SUCCESS) )
    {
        /** Retrieve Calculation Information **/
        if ((dsInfo.bRetrieveBalance) && (idReturn == ER_SUCCESS))
        {
           idReturn = X51013_RetrieveAccountBalances( lpBhvrCom,
                                                       lpVoid,
                                                       lpDS,
                                                       &dsInfo );
        }
        if (idReturn == ER_SUCCESS)
        {
           statement;
        }
    } /* End Processing */
 }

/***************************************************************
 * Function Clean Up
 ***************************************************************/
   if ( (dsInfo.hUser) != (HUSER) NULL )
   {
      statement;
   }

   return idReturn;

5.10 Terminating a Function

Always return a value with the termination of a function.