Prototype may be used to give a variable of a custom data type an ability to clone or make a copy of itself.
Such cloning should be treated on a type by type basis since cloning an existing string, for example, requires one set of considerations to be made while cloning an existing secure TCP/IP connection requires a different set of questions to be answered.
Yet in another case, if a current custom data type is composite and contains custom data types which are composite themselves and these contain composite custom data types and so on to an arbitrary depth then how can a variable of such a data type be cloned?
Each participating data type for which a bit-wize copy is not sufficient must implement its own cloning function. Observe that cloning of composite custom data types is similar in that respect to the process of turning a variable into an array of bytes via toBytes() discussed in Encapsulation Patterns chapter.
The Prototype pattern consequences:
Sample Problem
Implement the Prototype pattern for custom C strings from ADT chapter.
Generic Solution Description
1) Understand what exactly does it mean to clone a variable of a given data type.
2) Add a Clone() function or its equivalent to the data type's public interface.
3) Implement the cloning of the variable of a given data type.
Sample Solution
Step 1
To make a copy of an existing string means to:
1) allocate enough space to hold the contents of the original string
2) copy the contents of the original string into the new memory location
Step 2
Our clone function accepts two arguments:
1) a pointer to a variable to make a copy of
2) a custom memory location to create a new variable at
Add the prototypes of the new functions to libstr.h:
#include <sys/types.h> typedef struct str { int ( *cmp )( struct str*, struct str*, int ); size_t ( *length )( struct str* ); struct str* ( *clone )( struct str*, void* ); } str_t; extern str_t* strNew( const char* ); extern void strDelete( str_t** ); extern str_t* strConstruct( void*, const char* ); extern void strDestruct( str_t* ); extern size_t strSizeOf(); extern int strCmp( str_t*, str_t*, int ); extern size_t strLength( str_t* ); extern str_t* strClone( str_t*, void* );
Step 3
Implement the algorithm outlined in Step 1 in the libstr.c file.
For this particular data type we can implement cloning in terms of existing functions:
static str_t* strCloneImpl( str_t*, void* ); static str_t strAdt = { strCmpImpl, strLengthImpl, strCloneImpl }; extern str_t* strClone( str_t* src, void* mem ) { str_t* rv; rv = str->clone( src, mem ); return rv; } static str_t* strCloneImpl( str_t* prot, void* mem ) { str_t* rv; strimpl_t* simpl = ( strimpl_t* )prot; printf( "%s:%d: strCloneImpl(): cloning \"%s\"\n", __FILE__, __LINE__, src->buf ); if ( !mem ) { rv = strNew( simpl->buf ); } else { rv = strConstruct( mem, simpl->buf ); } return rv; }
Step 4
Rebuild the custom string library:
gcc -g -c -fPIC -I . libstr.c gcc -g -shared -o libstr.so libstr.o
Step 5
Modify the sample application to exercise the Prototype pattern.
str.c:
#include <stdio.h> #include "libstr.h" extern int main( int argc, char* argv[] ) { str_t* s1; str_t* s2; if ( argc != 2 ) { return 1; } s1 = strNew( argv[ 1 ] ); s2 = strClone( s1, NULL ); printf( "Strings s1 and s2 are %s equal.\n", strCmp( s1, s2, 1 ) ? "not" : "" ); printf( "strLength( s1 ) = %zu\n", strLength( s1 ) ); printf( "strLength( s2 ) = %zu\n", strLength( s2 ) ); strDelete( &s1 ); strDelete( &s2 ); return 0; }
Step 6
Rebuild the sample application:
gcc -g -c -I . str.c gcc -g -L . -o str str.o -lstr
Step 7
Run the sample program with various inputs:
./str "Hello, world!" libstr.c:224: strCloneImpl(): cloning "Hello, world!" libstr.c:200: strCmpImpl( "Hello, world!", "Hello, world!", 1 ) Strings s1 and s2 are equal. strLength( s1 ) = 13 strLength( s2 ) = 13
Exercises
1) Implement the Prototype pattern for a custom data type of your choice.
Files
\(\blacksquare\)