Chapter 2
Using SmartHeap

This chapter explains how to install and get started with SmartHeap. It then proceeds into an in-depth discussion of each of the SmartHeap memory allocation API families. Several more advanced topics in SmartHeap, such as memory pools and error handling, are also examined. Finally, this chapter discusses how to incorporate SmartHeap into your own product for distribution.

2.1 Installing SmartHeap

The SmartHeap distribution disk or tape contains C and C++ header files, statically linkable library files, sample applications, and source files for the SmartHeap definitions that override malloc and C++ operator new. Some platforms also contain additional files, such as dynamic link libraries (DLLs or shared libraries).

2.1.1 SmartHeap files common to all platforms

The following files are included on the SmartHeap distribution media for all platforms:

Directory File Description

include smrtheap.h Header file containing all SmartHeap C declarations except malloc.

include smrtheap.hpp Header file containing SmartHeap C++ declarations, including operator new.

Directory File Description

include shmalloc.h Header file containing declarations for SmartHeap ANSI C functions (malloc, etc.).

include heapagnt.h Header file containing all Debug SmartHeap C declarations (heapagnt.h is automatically included by the other SmartHeap header files when MEM_DEBUG is defined).

source shmalloc.c SmartHeap definition for ANSI C (malloc, etc.) functions — provided so you can customize these.

source shnew.cpp SmartHeap definition for operator new — provided so you can customize and/or build a library for non-supported compilers.

source def*.c, Default memory pool definitions

db*.c used by the above malloc and new definitions.

samples Various sample SmartHeap applications — mostly 16-bit Windows specific applications, but the SmartHeap techniques illustrated apply to all platforms.

For the SmartHeap library files names and platform-specific installation instructions, see the Getting Started and Platform Guide.

Important! Please read the ASCII text file readme.txt for information that updates this manual (the file may be named “README” or “READ ME” on some platforms).

2.2 Getting started with SmartHeap

This section covers the basics of using SmartHeap. The information here is very important, and is applicable no matter which SmartHeap features you choose to use in your application.

2.2.1 SmartHeap header files

If you plan to use only the ANSI C malloc, calloc, realloc, and free functions, plus C++ global operators new and delete, you don’t need to include SmartHeap header files or make any other changes to your source code to use SmartHeap — just relink, as described in the next section.

Note Simply linking with the SmartHeap Library is sufficient to cause your application to use the SmartHeap versions of C and C++ memory allocation functions. Include SmartHeap header files only if you want to use other SmartHeap APIs, if you want to create memory pools, if you want SmartHeap to include file and line information in error reports, or if you want to use the SmartHeap debugging APIs.

If you want to use other SmartHeap features, you need to include a SmartHeap header file in each of your source modules that references SmartHeap functions:

API

Advantages

Disadvantages

See

ANSI C

Fast; ANSI standard; variable-size blocks.

Some overhead (1-8 bytes per allocation); local fragmentation possible; max block size UINT_MAX.

§2.3.2 §2.3.3 §4.1.3

C++

Fast; standard; variable-size blocks; can overload to use any SmartHeap allocator.

Some overhead (1-8 bytes per allocation); local fragmentation possible; max block size UINT_MAX unless overloaded.

§2.3.4 §4.1.4

Fixed-size

Fastest API; zero overhead; no fragmen-
tation; can partition into memory pools.

Suitable only where numerous objects of the same size are required.

§2.3.5 §4.1.6

Pointer-based

Fast; variable-size blocks; can partition into pools to improve locality, reduce fragmentation, and simplify freeing; ULONG_MAX blocks.

Some overhead (1-8 bytes per allocation); local fragmentation possible, but manageable with memory pools.

§2.3.6 §4.1.5

Handle-based

Can be used to reduce or eliminate fragmen-
tation in variable-size pools; can partition into pools; ULONG_MAX blocks.

Requires locking/ unlocking memory handles; slowest; more overhead (10-20 bytes per allocation).

§2.3.7 §4.1.7

If your primary concern is speed of learning or minimal changes to your application, then you can use the standard (ANSI C or C++) APIs. To achieve optimal memory utilization and performance, consider using the SmartHeap APIs. If your application is designed in a layered fashion (as required for true portability), there’s no reason why you can’t take advantage of SmartHeap’s specialized allocators in your lower-level layer, without changing any code in the portable, higher layer(s).

Note In practice, you’ll probably want to combine two or more of these APIs for different data structures within your application. For best memory utilization and greatest efficiency, use the fixed-size API for all situations with several kilobytes or more of related allocations of the same size, and use the pointer-based API for all other allocations. This applies either to C or to C++. For C++ applications, you can create both fixed-size allocations and variable-size pointer-based allocations by overloading operator new. See the example for new in §4.2, “Function reference.”

2.3.2 ANSI C memory API

The most straightforward approach for using SmartHeap is to use the ANSI C API (malloc, calloc, realloc, and free). In this case, you don’t even need to include SmartHeap header files or otherwise make your source files specific to SmartHeap — just relink with the SmartHeap Library.

Important! For SmartHeap to successfully override malloc, etc., you must specify the SmartHeap Library before your compiler’s C runtime library on the linker command line. If the linker complains that SmartHeap_malloc is an unresolved external, then you’re not successfully linking SmartHeap’s malloc.

Note SmartHeap always statically links malloc, etc. even if you use a DLL version of SmartHeap. If you’re using SmartHeap in a DLL, you should never export SmartHeap malloc from your DLL to other DLLs or EXEs, because the function needs to refer to a statically linked global variable.

Windows 16-bit In 16-bit Windows, if you call SmartHeap malloc, etc. from a DLL, SmartHeap allocates shared memory that will be freed automatically when the DLL terminates. If you call malloc from an EXE, SmartHeap allocates non-shared memory — the memory is owned by the current Windows task and is freed automatically when that task terminates.

Note For 16-bit x86 versions, SmartHeap can override malloc only in the large memory model. If you want to use SmartHeap malloc in another memory model, you must either use SmartHeap’s macro version of malloc or use _fmalloc/farmalloc, which SmartHeap also overrides if they’re defined by your compiler. See §2.3.3 for instructions.

Here is how you use the ANSI C API:

#include <stdlib.h>
#include <string.h>
#define TRUE 1
#define FALSE 0

int DisplayError(char *msg, int num)
{
/* allocate buffer to format message in */
char *buf = (char *)malloc(strlen(msg)+20);

/* return if allocation failed */
if (buf == NULL)
return FALSE;

/* format and display the message */
sprintf(buf, "Error #%d: %s.", num, msg);
MessageBox(GetFocus(), buf, NULL, MB_OK);

/* free the buffer */
free(buf);

return TRUE;
}

2.3.3 If you do not want SmartHeap to override malloc

By default SmartHeap overrides malloc and the other ANSI C functions. If you want this default behavior, then skip this section. We recommend that you have SmartHeap override malloc to improve overall memory management efficiency and to avoid having two memory managers linked into your application.

Some reasons why you might not want SmartHeap to override malloc include:

To prevent SmartHeap from overriding the malloc function:

    Define the int global variable SmartHeap_malloc at file scope in your application.

    Either remove the shmalloc object module from the SmartHeap Library, or place your compiler’s runtime library before the SmartHeap Library on the linker command line.

If you’re avoiding overriding malloc due to a compiler runtime library conflict (including a memory model conflict for 16-bit x86), you can still have your calls to malloc go through SmartHeap, if you want. Just include the header file shmalloc.h in each of your source files. shmalloc.h uses macro substitution to replace the ANSI C function names. This technique avoids possible conflicts with your compiler’s runtime library, because the malloc, etc. functions are still the compiler definitions.

Important! If you include both shmalloc.h and stdlib.h or any other header files that declare malloc, you must include shmalloc.h after the other header files. Otherwise, the malloc macro defined in shmalloc.h will be expanded when stdlib.h is included, resulting in a syntax error in that header file.

Caution! To avoid mixing SmartHeap and compiler runtime library versions of these functions, be sure to include shmalloc.h in each of your source modules. For example, if you were to allocate memory using SmartHeap malloc in one module then free the memory with your compiler’s free in another module, you’d be faced with certain disaster.

Note For 16-bit x86 versions of SmartHeap, another alternative to the macro version of malloc is to use the far versions of malloc, which SmartHeap also overrides by default. The far functions are called either _fmalloc or farmalloc — they’re far functions that accept/return far pointers regardless of memory model, just like SmartHeap malloc. See your compiler library documentation to determine which function to use.

Here is a 16-bit Windows example that works in small and medium models:

#include <windows.h>
/* define malloc, etc. as macros */
#include <shmalloc.h>

/* define if shmalloc not linked */
int SmartHeap_malloc;

BOOL DisplayError(char far *msg, int num)
{
/* allocate buffer to format message in */
char far *buf =
(char far *)malloc(lstrlen(msg)+20);

if (buf == NULL)
return FALSE; /* allocation failed */

/* format and display the message */
wsprintf(buf, "Error #%d: %s.", num, msg);
MessageBox(GetFocus(), buf, NULL, MB_OK);

/* free the buffer */
free(buf);
return TRUE;
}

2.3.4 C++ memory API

SmartHeap defines several versions of the C++ new and delete operators. If you use only the standard global new and delete operators, you don’t need to include any SmartHeap header files or make any changes to your source files — just relink with the SmartHeap Library.

To use SmartHeap’s overloaded versions of operator new, or to define your own operators new and delete in terms of SmartHeap, you need to include the SmartHeap C++ header file smrtheap.hpp.

Important! For SmartHeap to successfully override operator new, you must specify the SmartHeap Library before your compiler’s C++ runtime library on the linker command line.

If you have no references to SmartHeap’s overloaded operator new and you’re having trouble getting your linker to resolve to SmartHeap new rather than your compiler’s new, place a reference to the global variable SmartHeap_new inside one of your functions.

For example:

#include <smrtheap.hpp>

int main()
{
/* This reference to SmartHeap_new will ensure
that SmartHeap operator new gets linked in
rather than the C++ compiler's.
*/
SmartHeap_new = 1;

/* . . . */
}

Window 16-bit In 16-bit Windows, if you call SmartHeap operator new from a DLL, SmartHeap allocates shared memory that will be freed automatically when the DLL terminates. If you call new from an EXE, SmartHeap allocates non-shared memory — the memory is owned by the current Windows task and is freed automatically when that task terminates.

If you want to use SmartHeap from a C++ application but don’t want to have it redefine global operators new and delete, then you’ll need to include smrtheap.h rather than smrtheap.hpp in your source modules. To avoid linking in SmartHeap operator new, you’ll need to either place the SmartHeap Library after your C++ compiler’s runtime library on the linker command line, or else remove the object module shnew from the SmartHeap Library.

SmartHeap defines an alternative operator new that uses the placement syntax for specifying a memory pool. You can also define new and delete for your specific classes. You can use both these techniques in any memory model. See the entry for new in §4.2, “Function reference,” for a detailed explanation and examples. The C++ sample application on the SmartHeap distribution media also illustrates these techniques.

Note For 16-bit x86, SmartHeap also overloads far and huge versions of operator new, if your compiler defines them. Large model is recommended for 16-bit platforms. If you use a memory model other than large, you’ll need to link with your compiler’s global operator new rather than SmartHeap’s. To allocate memory from SmartHeap, you’ll need to either use the far/huge versions of operator new or use the placement syntax with operator new to allocate from a SmartHeap memory pool.

Also note SmartHeap also defines special versions of operator new for certain platforms. For example, SmartHeap provides integration with the Borland C++ ObjectWindows Library and the Microsoft Foundation Class Library that each include overloaded operator new definitions. For more information, see the Getting Started and Platform Guide.

2.3.5 Fixed-size API

The most important factor in choosing an appropriate allocator is the nature of the data structures that the dynamic memory will contain. If the data structure is made up of many small, fixed-size structures (or class objects), as in the case of lists and trees, the most appropriate allocator is the fixed-size allocator. SmartHeap fixed-size allocations involve absolutely no overhead (meaning header information), aren’t subject to fragmentation (because each block allocated is of the same size), and are very fast (fixed-size allocations involve simply popping the head of a linked list).

Use MemPoolInitFS to create a memory pool from which blocks of a fixed size are allocated. Use MemAllocFS to allocate fixed-size blocks from a pool and MemFreeFS to free the fixed-size blocks.

Here’s an example of how to use SmartHeap’s fixed-size allocator:

#include <smrtheap.h>

/* example list link structure */
typedef struct _Link
{
int value;
struct _Link *next;
} Link;

/* Initialize fixed-size memory pool for links. */
MEM_POOL CreateList(int InitialCount)
{
return MemPoolInitFS(sizeof(Link),
InitialCount, MEM_POOL_DEFAULT);
}

/* add a new link to a list */
Link *InsertLink(MEM_POOL LinkPool,
Link *prev,int val)
{
/* allocate a new Link */
Link *link = (Link *)MemAllocFS(LinkPool);
if (link == NULL)
return NULL; /* allocation failed */

/* initialize the new link */
link->value = val;
if (prev)
{
link->next = prev->next;
prev->next = link;
}
else
link->next = NULL;

return link;
}

/* free entire list */
int FreeList(MEM_POOL LinkPool)
{
/* Freeing the pool frees all memory in the list
in one call: much faster and less error prone
than freeing each element individually. */
return MemPoolFree(LinkPool);
}

Note Beginning in version 3.0, for all blocks under 256 bytes in size, SmartHeap automatically uses an algorithm as fast as the fixed-size algorithm, so you seldom need to use the fixed-size API to achieve good performance.

2.3.6 Pointer-based API

If a data structure requires a one-time allocation of a variable size (for example, an array or temporary buffer), or if the memory block may be changed in size (re-allocated), then a fixed-size allocator isn’t appropriate. SmartHeap’s implementation of ANSI C malloc often is appropriate.

SmartHeap also defines a similar, but more flexible, alternative that is referred to in this manual as the pointer-based API. The pointer-based API permits you to group allocations into memory pools, which:

Windows 16-bit · When the 16-bit Windows SmartHeap DLL is initialized, it spawns a background task, sh30mon.exe, to monitor task termination. If sh30mon.exe doesn’t exist, it’s automatically created by the SmartHeap DLL. However, if the SmartHeap DLL is installed on a network with read-only access, then SmartHeap will be unable to create the EXE file and will thus fail to initialize. Therefore, you may want to distribute sh30mon.exe along with the SmartHeap DLL.


SmartHeap Programmer’s Guide 55