Non-integral are the parts that are not embedded in a whole permanently. At run time the whole and its non-integral parts exist in separate blocks of memory.
If a whole has to manage the life time of a non-integral part then we will say that the whole owns that part or that this non-integral part is owned by its whole. When a whole is created all of its non-integral owned parts are created also. Consequently, when a whole is deleted all of its non-integral owned parts are also deleted.
If a whole does not have to manage the life time of a non-integral part then we will say that the whole rents that part or that this non-integral part is rented by its whole. In this case a whole completely ignores all of its non-integral rented parts in its life cycle functions.
Non-integral Owned Parts
Sample Problem
Make str_t ADTs - book's title and authors - private non-integral owned parts of a book_t ADT.
Generic Solution Description
1) Add one pointer to a non-integral owned part for each and all such parts in the whole's implementation data type.
2) Exclude all the non-integral owned parts from the whole's memory allocation budget in the whole's SizeOf() function.
3) Create all the non-integral owned parts in the whole's Construct() function.
4) Delete all the non-integral owned parts in the whole's Destruct() function.
Sample Implementation
Step 1
Borrow the relevant files from Integral Parts chapter:
Step 2
Since we will not be using the logicalSize() family of functions from Integral Parts chapter we will delete them from libbook.c. You may keep these functions if you wish.
Step 3
We will use the latest implementation of bookimpl_t which includes authors as an additional integral part.
As such, exclude title and authors from the bookimpl_t's size calculation in bookSizeOf() in libbook.c:
extern size_t bookSizeOf() { size_t rv; rv = sizeof( bookimpl_t ); return rv; }
Step 4
Once the whole is created and ready to be returned to the user, create the non-integral owned parts in bookConstruct() in libbook.c:
extern book_t* bookConstruct( void* mem, const char* title, const char* authors ) { bookimpl_t* bimpl = ( bookimpl_t* )mem; bimpl->bookadt = bookAdt; bimpl->title = strNew( title ); bimpl->authors = strNew( authors ); return &bimpl->bookadt; }
Step 5
Once the whole is destructed, delete the non-integral owned parts in bookDestruct() in libbook.c:
extern void bookDestruct( book_t* book ) { bookimpl_t* bimpl = ( bookimpl_t* )book; strDelete( &bimpl->title ); strDelete( &bimpl->authors ); }
Step 6
Adjust bookInfoImpl() in libbook.c to reflect the change in the parts' ownership type.
Observe that in Integral Parts chapter title and authors, as str_t*, pointed to memory locations inside the bookimpl_t block. As such, to print the addresses of title and authors we used the bimpl->title and bimpl->authors constructs.
In contrast, the now non-integral parts do not reside in bookimpl_t's memory block anymore. Consequently, to print their addresses we use the "address of" C operator, &, in the &bimpl->title and &bimpl->authors constructs in libbbok.c:
extern void bookInfoImpl( book_t* book ) { bookimpl_t* bimpl = ( bookimpl_t* )book; printf( "%s:%d: bookInfoImpl()\n", __FILE__, __LINE__ ); printf( " bookSizeOf() = %zu\n", bookSizeOf() ); printf( " sizeof( bookimpl_t ) = %zu\n", sizeof( bookimpl_t ) ); printf( " sizeof( book_t ) = %zu\n", sizeof( book_t ) ); printf( " sizeof( title ) = %zu\n", sizeof( str_t* ) ); printf( " sizeof( authors ) = %zu\n", sizeof( str_t* ) ); printf( " &book = %p\n", ( void* )book ); printf( " &bookadt = %p\n", ( void* )( &bimpl->bookadt ) ); printf( " &title = %p\n", ( void* )( &bimpl->title ) ); printf( " &title - &book = %zu\n", ( void* )( &bimpl->title ) - ( void* )book ); printf( " &authors = %p\n", ( void* )( &bimpl->authors ) ); printf( " &authors - &title = %zu\n", ( void* )( &bimpl->authors ) - ( void* )( &bimpl->title ) ); }
Step 7
Rebuild the libbook.so shared library (using Chained Linking):
gcc -g -c -fPIC -I . libbook.c gcc -g -L . -shared -o libbook.so libbook.o -lstr
Step 8
Run the book sample application. Observe that there is no need to rebuild it:
./book libbook.c:100: bookInfoImpl() bookSizeOf() = 24 sizeof( bookimpl_t ) = 24 sizeof( book_t ) = 8 sizeof( title ) = 8 sizeof( authors ) = 8 &book = 0xb3a010 &bookadt = 0xb3a010 &title = 0xb3a018 &title - &book = 8 &authors = 0xb3a020 &authors - &title = 8
Files
Non-integral Rented Parts
Normally, all the non-integral rented parts are created, constructed, destructed and deleted elsewhere outside the whole. In these scenarios a whole is not called upon to manage the life cycle of its non-integral rented parts.
Even though non-integral rented parts are pointers in the whole's implementation structure, the whole ignores that fact in its New(), Construct(), Destrcut(), Delete() functions. See the Decorator pattern for an example.
In certain cases a whole may specifically be charged with creating some part(s) via this part's New() function. See the Builder pattern for an example. However, these parts are still qualified as "rented" and not "owned" as they are not created and deleted in unison with the whole. Instead, such parts' life cycle is controlled elsewhere outside and beyond the reach of the whole - users decide when these types of parts come into existence and when they vanish.
The Builder pattern, mentioned earlier, is used to absorb and centralize a broader functionality of assembling an arbitrarily complex entity out of multitude of subordinate parts. The usage of Builder also implies that after a while a fully assembled entity will be separated or detached from the object (variable) that created it.
Sample Problem
Make book's title and authors private non-integral rented parts of a book_t ADT.
Generic Solution Description
1) Add one pointer to a non-integral rented part for each and all such parts in the whole's implementation data type.
2) Exclude all the non-integral rented parts from the whole's memory allocation budget in the whole's SizeOf() function.
3) Ignore all the non-integral rented parts in the whole's New(), Construct(), Destruct(), Delete() functions.
4) Add an appropriate public interface to a whole to add/remove, set/unset, attach/detach its non-integral rented parts.
Sample Implementation
Step 1
Borrow the files from the previous section, non-integral owned parts, by copying them to the corresponding directory, for example.
Step 2
Since str_t now belongs to book_t's public interface, move libstr.h from libbook.c to libbook.h:
#include <sys/types.h> #include "libstr.h"
Step 3
Add a public interface to book_t to set its title and authors in libbook.h:
#include <sys/types.h> #include "libstr.h" typedef struct book { void ( *info )( struct book* ); str_t* ( *setTitle )( struct book*, str_t* ); str_t* ( *setAuthors )( struct book*, str_t* ); } book_t;
Step 4
Remove the title and authors C strings from the prototype of bookNew() and bookConstruct() in libbook.h:
#include <sys/types.h> #include "libstr.h" typedef struct book { void ( *info )( struct book* ); str_t* ( *setTitle )( struct book*, str_t* ); str_t* ( *setAuthors )( struct book*, str_t* ); } book_t; extern book_t* bookNew(); extern void bookDelete( book_t** ); extern book_t* bookConstruct( void* );
Step 5
Add the prototypes for the corresponding convenience functions in libbook.h:
#include <sys/types.h> #include "libstr.h" typedef struct book { void ( *info )( struct book* ); str_t* ( *setTitle )( struct book*, str_t* ); str_t* ( *setAuthors )( struct book*, str_t* ); } book_t; extern book_t* bookNew(); extern void bookDelete( book_t** ); extern book_t* bookConstruct( void* ); extern void bookDestruct( book_t* ); extern size_t bookSizeOf(); extern void bookInfo( book_t* ); extern str_t* bookSetTitle( book_t*, str_t* ); extern str_t* bookSetAuthors( book_t*, str_t* );
Step 6
Add the prototypes of the implementation functions of the new public interface to book_t in libbook.c:
static void bookInfoImpl( book_t* ); static str_t* bookSetTitleImpl( book_t*, str_t* ); static str_t* bookSetAuthorsImpl( book_t*, str_t* ); static book_t bookAdt = { bookInfoImpl, bookSetTitleImpl, bookSetAuthorsImpl };
Step 7
bookSizeOf() in libbook.c already excludes title and authors from bookimpl_t's memory allocation budget:
extern size_t bookSizeOf() { size_t rv; rv = sizeof( bookimpl_t ); return rv; }
Step 8
Remove the creation of title and authors from bookNew(), bookConstruct() and their deletion from bookDestruct() in libbook.c:
extern book_t* bookNew() { book_t* book = ( book_t* )NULL; void* mem = calloc( 1, bookSizeOf() ); book = bookConstruct( mem ); return book; } extern book_t* bookConstruct( void* mem ) { bookimpl_t* bimpl = ( bookimpl_t* )mem; bimpl->bookadt = bookAdt; bimpl->title = NULL; bimpl->authors = NULL; return &bimpl->bookadt; } extern void bookDestruct( book_t* book ) { bookimpl_t* bimpl = ( bookimpl_t* )book; bimpl->title = NULL; bimpl->authors = NULL; }
Step 9
Implement the new interface in libbook.c:
extern str_t* bookSetTitle( book_t* book, str_t* title ) { str_t* oldTitle; oldTitle = book->setTitle( book, title ); return oldTitle; } extern str_t* bookSetAuthors( book_t* book, str_t* authors ) { str_t* oldAuthors; oldAuthors = book->setAuthors( book, authors ); return oldAuthors; } extern str_t* bookSetTitleImpl( book_t* book, str_t* title ) { str_t* oldTitle; bookimpl_t* bimpl = ( bookimpl_t* )book; oldTitle = bimpl->title; bimpl->title = title; return oldTitle; } extern str_t* bookSetAuthorsImpl( book_t* book, str_t* authors ) { str_t* oldAuthors; bookimpl_t* bimpl = ( bookimpl_t* )book; oldAuthors = bimpl->authors; bimpl->authors = authors; return oldAuthors; }
Step 10
Build the libbook.so shared library.
In this particular scenario str_t is used as a means of public communication between two components - libbook.so and book. Since book is a program, it will have to specify libstr.so as its dependency because otherwise link editor will fail to assemble its executable image because of the unresolved references to strNew() and strDelete(). Being a shared library, libbook.so does not have to specify libstr.so as its dependency.
As such, to eliminate the potential code duplication, build libbook.so without the dependency on libstr.so:
gcc -g -c -fPIC -I . libbook.c gcc -g -shared -o libbook.so libbook.o
Step 11
Adjust the sample application to reflect the change in the book's parts' ownership type in book.c:
#include <stdio.h> #include "libbook.h" extern int main( int argc, char* argv[] ) { book_t* book = bookNew(); str_t* title = strNew( "The C Programming Language" ); str_t* authors = strNew( "K & R" ); bookInfo( book ); bookSetTitle( book, title ); bookSetAuthors( book, authors ); bookDelete( &book ); strDelete( &title ); strDelete( &authors ); return 0; }
Step 12
Build the book sample application.
For the reasons explained earlier make libstr.so a dependency of book on the link line:
gcc -g -c -I . book.c gcc -g -L . -o book book.o -lbook -lstr
Step 13
Run book:
./book libbook.c:113: bookInfoImpl() bookSizeOf() = 40 sizeof( bookimpl_t ) = 40 sizeof( book_t ) = 24 sizeof( title ) = 8 sizeof( authors ) = 8 &book = 0x13e2010 &bookadt = 0x13e2010 &title = 0x13e2028 &title - &book = 24 &authors = 0x13e2030 &authors - &title = 8
Exercises
1) Add bookGetTitle() and bookGetAuthors() interfaces to book_t version that implements title and authors as non-integral rented parts.
Files
\(\blacksquare\)