/* Creating structs on the heap, * and passing them to methods. * * This is the SAME memory-model that Java uses: * all objects are really object-references. * */ #include #include #include #include #include #include #define DBG(...) 0 //printf(__VA_ARGS__) #define STREQ(a,b) (strcmp((a),(b))==0) #define STRNEQ(a,b) (!STREQ(a,b)) /* * Things to note: * -'new_Cat' returns a `Cat*` (not a `Cat`) * -toString -- WE must allocate enough space for the result!! * -Instead of Java `c.name`, it's (*c).name or c->name * * less important: * const -- cool! * typedef -- cool! * Note no overloading, so I wrote `new_Cat` and `new_Cat2` * * v.v. uninmportant * #includes; macros. * enum->string voodoo */ enum CatColor { BLACK, GREY, ORANGE, CALICO, TORTOISESHELL }; const char * const CatColor_str[] = { [BLACK] = "BLACK" ,[GREY] = "GREY" ,[ORANGE] = "ORANGE" ,[CALICO]="CALICO" ,[TORTOISESHELL]="TORTOISESHELL" }; // h/t: https://stackoverflow.com/a/58500930/320830 // safety check: static_assert( (sizeof(CatColor_str)/sizeof(CatColor_str[0]) == (TORTOISESHELL+1)), "CatColor enum has different size than CatColor_str" ); typedef enum CatColor CatColor; struct Cat { char* name; CatColor color; float clawSharpness; // A number between 0 and 1. }; typedef struct Cat Cat; // How much scratching might sharpen your claws. // The fraction between current-sharpness and perfect-sharpness (1.0) to improve. // const float SHARPENING_FACTOR = 0.10f; // Update this Cat, after it scratches something; // it may be something that `sharpens` the claws (like a tree), // or something that slightly dulls them (like human flesh). // void scratch( Cat* thiss, bool sharpens ) { if (! sharpens) { printf("Yeeowch!\n"); // presumably using their claws on a person thiss->clawSharpness -= SHARPENING_FACTOR*thiss->clawSharpness; } else { // get (say) 25% closer to full sharpness thiss->clawSharpness += SHARPENING_FACTOR*(1.0f-thiss->clawSharpness); } } void testAll(); // forward declaration int main( int argc, char* argv[] ) { testAll(); } // @Override char* catToString( Cat* c ) { const int sz = strlen("Cat[") + strlen("name='") + strlen((*c).name) + strlen("', ") + strlen("color=") + strlen(CatColor_str[(*c).color]) + strlen(", ") + strlen("clawSharpness=") + 4 + strlen("]") +1; char* buffer = malloc(sz * sizeof(char)); // THINK of this as `char[] buffer = …` snprintf(buffer, sz, "Cat[name='%s', color=%s, clawSharpness=%4.2f]", c->name, CatColor_str[c->color], c->clawSharpness); DBG("sz is %d; buffer is: %s.\n", sz, buffer); return buffer; } // Canonical constructor Cat* new_Cat( char* _name, CatColor _color, float _clawSharpness ) { Cat* kitten = malloc(sizeof(Cat)); (*kitten).name = _name; (*kitten).color = _color; (*kitten).clawSharpness = _clawSharpness; return kitten; } // constructor w/ default clawSharpness Cat* new_Cat2( char* _name, CatColor _color ) { return new_Cat(_name, _color, 0.50f); } const float TOLERANCE = 0.000001f; bool equalCats( Cat* thiss, Cat* that ) { if (that == NULL) return false; else if (thiss == that) return true; // hot path -- not for correctness else return STREQ(thiss->name, that->name) && (thiss->color == that->color) && fabs(thiss->clawSharpness - that->clawSharpness) < TOLERANCE; } static void testConstructor() { printf("Testing constructor…"); // In practice, we might bundle this with tests of toString and equals. // (And if we used `record` instead of `class`, we would't need to!) // making sure our 'equals' works: assert( equalCats( new_Cat( "bartok", BLACK, 0.9f ), new_Cat( "bartok", BLACK, 0.9f ) )); assert(! equalCats( new_Cat( "bartok", BLACK, 0.9f ), new_Cat( "brahms", ORANGE, 0.9f ) )); // check that default constructor uses 0.50f sharpness: assert( equalCats( new_Cat2( "bartok", BLACK), new_Cat( "bartok", BLACK, 0.5f ) )); printf("done\n"); } static void testCatToString() { printf("Testing CatToString…"); assert( STREQ( catToString( new_Cat( "pepsi", CALICO, 0.50f)), "Cat[name='pepsi', color=CALICO, clawSharpness=0.50]" )); assert( STREQ( catToString( new_Cat( "pepsi", CALICO, 0.1234f)), "Cat[name='pepsi', color=CALICO, clawSharpness=0.12]" )); assert( STREQ( catToString( new_Cat2( "bartok", BLACK) ), "Cat[name='bartok', color=BLACK, clawSharpness=0.50]" )); printf("done\n"); } static void testScratch() { printf("Testing scratch…"); Cat* bartok = new_Cat( "Bartok", BLACK, 0.80f ); Cat* garfield = new_Cat( "Garfield", ORANGE, 0.50f ); // NOTE tests are tuned for SHARPENING_FACTOR = 0.10; // must re-write some if that changes. scratch(garfield, false); assert( equalCats(garfield, new_Cat("Garfield", ORANGE, 0.45f) ) ); scratch(garfield, false); assert( equalCats(garfield, new_Cat("Garfield", ORANGE, 0.45f-0.045f ) ) ); assert( equalCats(bartok, new_Cat("Bartok", BLACK, 0.80f) ) ); // With mutation, testing *includes* checking that we didn't // modify other fields, NOR other fields of other objects // (e.g. that we didn't accidentally declare a field `static`). scratch(bartok,true); assert( equalCats(bartok, new_Cat("Bartok", BLACK, 0.82f) ) ); printf("done\n"); } void testAll() { testConstructor(); testCatToString(); testScratch(); } /** * @author ibarland * @version 2025-Jan-23 * * @license CC-BY -- share/adapt this file freely, but include attribution, thx. * https://creativecommons.org/licenses/by/4.0/ * https://creativecommons.org/licenses/by/4.0/legalcode * Including a link to the *original* file satisifies "appropriate attribution". */