Re: Optional args in C code.

"Mark Hahn" <[email protected]> Sun, 2 May 2004 23:39:22 -0700
Newsgroups gmane.comp.lang.prothon.devel
Message-ID <[email protected]>
Ben: See his SVN problems below...

Joe Knapka wrote:

> Hi guys,
>
> I'm being a bit myopic at the moment, just trying to finish up the
> string methods without getting caught up in the bigger picture.  So
> while I could probably find the answer to this myself, I'm sleepy, and
> would appreciate it if someone could answer this:
>
> How do I declare a C implementation of a Prothon method that takes
> optional args?  For example, the C code implementing
> String.find("string_to_find",index_to_start_looking) needs a default
> value of 0 for index_to_start_looking.  I thought it might be as
> simple as expecting the relevant entry in the parms[] array to be null
> if the arg isn't specified, but that doesn't work; the interpreter
> complains if I try to pass only one arg to find().

Try this:

DEF ( String, find, FPARM2( stringToFind, NULL,
                                               indexToStartLooking,
NEW_INT(0) ) ) {
        char* str;
        i32_t  index;

        STRING_PARAM(1, str);
        INT_32_PARAM(2, index);

      blah blah blah
}

FPARM2 says to use two formal params.  The first will be named stringToFind
and have no default value.  The second will be named indexToStartLooking and
have a default value of 0.  This is equivalent to:

def find(stringToFind, indexToStartLooking = 0):
    blah blah blah

The two macros ending in _PARAM will check the incoming params from the
parms array, check their type, and stuff the values into the variables ready
to use.

Shoule these names really be as long as  "indexToStartLooking"?  That is
unusually long.

If you want me to explain how these new macros work, let me know.  Clearly I
need to document these macros.

> This weekend, I implemented and tested (reasonably thoroughly) the
> following string methods, but haven't yet committed anything due to
> apparent lack of appropriate SVN permissions (or maybe I'm just not
> smart enough to use svn :-) :

Great!

Ben:  Please get his permissions fixed.  Get on the phone if you have to.

> lower
> upper
> replace
> capitalize
> expandtabs
> lstrip
> rstrip
> strip
> split
> find
>
> "svn diff" output follows, in case some one wants to commit
> the changes for me. Note, there doesn't seem to be any standard
> function corresponding to my "bin_strstr". Glibc has memmem(),
> but claims it's an extension, and broken to boot. But if
> someone can suggest a standard replacement, I'd appreciate
> it.
>
> -- Joe
>
> svn diff builtins-string.c
> Index: builtins-string.c
> ===================================================================
> --- builtins-string.c (revision 444)
> +++ builtins-string.c (working copy)
> @@ -53,6 +53,7 @@
>
>  // builtins.c
>
> +#include <ctype.h>
>  #include <stdio.h>
>  #include <string.h>
>
> @@ -353,7 +354,7 @@
>   } else {
>   obj_str = obj_malloc(ist, OBJ(STRING_PROTO), obj,
>   sizeof(pr_str_t)+tlen+1); if(!obj_str) {
> - raise_exception(ist, OBJ(OUTOFMEMORY_EXC), "memory allocation
> failed for string multiplication"); + raise_exception(ist,
>   OBJ(OUTOFMEMORY_EXC), "memory allocation failed for string join");
>   return NULL; }
>   obj_str->len = tlen;
> @@ -407,7 +408,312 @@
>   return NEW_INT(sizeof(pr_str_t) + pr_strlen(self) + 1);
>  }
>
> +// Generic case-changer function.
> +static obj_p changeCase(isp ist,obj_p self,int (*changer)(int),
> size_t howMany) { + size_t str_len = pr_strlen(self), ii = 0;
> + char* dest_ptr = 0;
> + obj_p obj=0;
> + pr_str_p obj_str=0;
>
> + obj = NEW_OBJ(String_OBJ);
> + obj_str = obj_malloc(ist, OBJ(STRING_PROTO), obj,
> sizeof(pr_str_t)+str_len+1); + if(!obj_str) {
> + raise_exception(ist, OBJ(OUTOFMEMORY_EXC),
> + "memory allocation failed for string.changeCase (used by lower(),
> upper(), etc.)"); + return NULL;
> + }
> + obj_str->len = str_len;
> + dest_ptr = obj_str->str;
> +
> + // Fill the copy.
> + char* src_ptr = pr_strptr(self);
> + for (ii=0; ii<str_len; ++ii) {
> + if (ii<howMany) {
> + dest_ptr[ii] = (char)(*changer)(src_ptr[ii]);
> + } else {
> + dest_ptr[ii] = src_ptr[ii];
> + }
> + }
> + dest_ptr[str_len] = 0;
> +
> + return obj;
> +}
> +
> +DEF(String, lower, NULL) {
> + return changeCase(ist,self,&tolower,pr_strlen(self));
> +}
> +
> +DEF(String, upper, NULL) {
> + return changeCase(ist,self,&toupper,pr_strlen(self));
> +}
> +
> +DEF(String, capitalize, NULL) {
> + return changeCase(ist,self,&toupper,1);
> +}
> +
> +// strstr()-analog that works for strings that may contain nulls.
> +// Totally dependent upon ASCII encoding; beware.
> +static char* bin_strstr(char* haystack,size_t lhaystack, char*
> needle,size_t lneedle) { + char* lookfor = needle;
> + char* looking_at = haystack;
> + char* hs_end = haystack+lhaystack;
> + char* ndl_end = needle+lneedle;
> + while (looking_at < hs_end) {
> + if (*lookfor == *looking_at) {
> + // Matched a character.
> + ++lookfor;
> + if (lookfor == ndl_end) {
> + // Matched the whole string. It started lneedle-1
> + // characters before the character we're looking at
> + // now.
> + return looking_at + 1 - lneedle;
> + }
> + } else {
> + // No match.
> + if (lookfor > needle) {
> + // We had recognized the beginning of a match. We must
> + // return to the character after the location at which
> + // the non-match began.
> + looking_at -= lookfor-needle;
> + lookfor = needle;
> + }
> + }
> + ++looking_at;
> + }
> + return 0;
> +}
> +
> +#define Int_value(objid) (objid->data.i64)
> +DEF(String, find, FORM_PARAM2) {
> + size_t str_len = pr_strlen(self);
> + size_t find_len = 0;
> + char* find_ptr = 0;
> + size_t start = 0;
> + obj_p result = 0;
> +
> + if (!has_proto_QUES(ist, parms[1], String_OBJ)) {
> + raise_exception(ist, OBJ(TYPE_EXC), "find function parameter 1 must
> be a string"); + return NULL;
> + }
> + if (parms[3]) {
> + if (!has_proto_QUES(ist, parms[3], OBJ(INT_PROTO))) {
> + raise_exception(ist, OBJ(TYPE_EXC), "find function parameter 2 must
> be an integer"); + return NULL;
> + } else {
> + start = (size_t)Int_value(parms[3]);
> + }
> + }
> +
> + find_ptr = pr_strptr(parms[1]);
> + find_len = pr_strlen(parms[1]);
> + if (find_len<1) {
> + return new_int_obj(ist,0);
> + }
> + char* found =
> bin_strstr(pr_strptr(self)+Int_value(parms[3]),str_len,find_ptr,find_len);
> + if (found) { + result = new_int_obj(ist,found-pr_strptr(self));
> + } else {
> + result = new_int_obj(ist,0);
> + }
> +
> + return result;
> +}
> +
> +DEF(String, replace, FORM_PARAM2) {
> + size_t str_len = pr_strlen(self);
> + size_t find_len = 0;
> + size_t repl_len = 0;
> + size_t result_len = str_len;
> + char* dest_ptr = 0;
> + obj_p obj=0;
> + pr_str_p obj_str = 0;
> + char* src_ptr = 0;
> + char* find_ptr = 0;
> + char* repl_ptr = 0;
> + size_t temp_len = 0;
> + char* a_match = 0;
> + char* where_ptr = 0;
> +
> + if (!has_proto_QUES(ist, parms[1], String_OBJ)) {
> + raise_exception(ist, OBJ(TYPE_EXC), "replace function parameter 1
> must be a string"); + return NULL;
> + }
> + if (!has_proto_QUES(ist, parms[3], String_OBJ)) {
> + raise_exception(ist, OBJ(TYPE_EXC), "replace function parameter 2
> must be a string"); + return NULL;
> + }
> +
> + find_len = pr_strlen(parms[1]);
> + if (find_len>str_len) return self;
> + if (find_len<1) {
> + raise_exception(ist, OBJ(TYPE_EXC), "replace: empty search string");
> + return NULL;
> + }
> + repl_len = pr_strlen(parms[3]);
> + src_ptr = pr_strptr(self);
> + find_ptr = pr_strptr(parms[1]);
> + repl_ptr = pr_strptr(parms[3]);
> +
> + // This is perhaps not as efficient as it could be.
> +
> + // Find the length of the result string.
> + temp_len = str_len;
> + a_match = bin_strstr(src_ptr,temp_len,find_ptr,find_len);
> + while (a_match) {
> + result_len -= find_len;
> + result_len += repl_len;
> + a_match += find_len; // Skip the matching text.
> + temp_len = str_len - (a_match - src_ptr);
> + a_match = bin_strstr(a_match,temp_len,find_ptr,find_len);
> + }
> +
> + // Create the copy.
> + // Create the copy.
> + obj = NEW_OBJ(String_OBJ);
> + if (result_len < IMMEDIATE_DATA_LEN) {
> + obj->data_type = DATA_TYPE_IMMDATA;
> + obj->imm_data_len = (int) result_len;
> + dest_ptr = obj->data.str;
> + } else {
> + obj_str = obj_malloc(ist, OBJ(STRING_PROTO), obj,
> sizeof(pr_str_t)+result_len+1); + if(!obj_str) {
> + raise_exception(ist, OBJ(OUTOFMEMORY_EXC), "memory allocation
> failed for string.replace"); + return NULL;
> + }
> + obj_str->len = result_len;
> + dest_ptr = obj_str->str;
> + }
> +
> + // Fill the replaced copy.
> + temp_len = str_len;
> + where_ptr = src_ptr;
> + a_match = bin_strstr(where_ptr,temp_len,find_ptr,find_len);
> + while (a_match) {
> + // Copy everything from where we were to the beginning of the
> + // match.
> + int frag_len = a_match-where_ptr;
> + memcpy(dest_ptr,where_ptr,frag_len);
> + dest_ptr += frag_len;
> +
> + // Copy the replacement text.
> + memcpy(dest_ptr,repl_ptr,repl_len);
> + dest_ptr += repl_len;
> +
> + a_match += find_len; // Skip the matched text.
> + where_ptr = a_match;
> + temp_len = str_len - (a_match - where_ptr);
> + a_match = bin_strstr(a_match,temp_len,find_ptr,find_len);
> + }
> +
> + // Copy any trailing text after the last match.
> + if (where_ptr<src_ptr+str_len) {
> + memcpy(dest_ptr,where_ptr,(src_ptr+str_len)-where_ptr);
> + }
> +
> + return obj;
> +}
> +
> +DEF(String, expandtabs, NULL) {
> + // FIX ME: should accept an int arg (default 8) telling how many
> + // spaces per tab.
> + obj_p tab = new_string_obj(ist,"\t");
> + obj_p spcs = new_string_obj(ist,"        ");
> + obj_p args[] = {NULL,tab,NULL,spcs};
> + obj_p result = Stringreplace( ist, self, 2, args, 0 );
> + del_unlock(tab);
> + del_unlock(spcs);
> + return result;
> +}
> +
> +//===============================
> +// ASSUMPTION: if we're using lstrip, rstrip, etc, then
> +// we're dealing with null-terminated text strings.
> +//===============================
> +DEF(String, lstrip, NULL) {
> + char* cstr = pr_strptr(self);
> + while (isspace(*cstr)) {
> + ++cstr;
> + }
> +
> + // Create the copy.
> + return new_string_obj(ist,cstr);
> +}
> +
> +DEF(String, rstrip, NULL) {
> + // Count whitespace at the end.
> + char* cstr = pr_strptr(self)+pr_strlen(self)-1;
> + size_t result_len = 0;
> + while (isspace(*cstr)) {
> + --cstr;
> + }
> + result_len = cstr-pr_strptr(self)+1;
> +
> + // Create the copy.
> + return new_string_n_obj(ist,pr_strptr(self),result_len);
> +}
> +
> +DEF(String, strip, NULL) {
> + char* cstr = pr_strptr(self);
> + while (isspace(*cstr)) {
> + ++cstr;
> + }
> +
> + // Count whitespace at the end.
> + char* cstr2 = pr_strptr(self)+pr_strlen(self)-1;
> + size_t result_len = 0;
> + while (isspace(*cstr2)) {
> + --cstr2;
> + }
> + result_len = cstr2-cstr+1;
> +
> + // Create the copy.
> + return new_string_n_obj(ist,cstr,result_len);
> +}
> +
> +DEF(String, split, FORM_RPARAM) {
> + size_t str_len = pr_strlen(self);
> + size_t find_len = 0;
> + char* src_ptr = 0;
> + char* find_ptr = 0;
> + size_t temp_len = 0;
> + char* a_match = 0;
> + char* where_ptr = 0;
> + obj_p list_obj = new_list_obj(ist,0);
> +
> + if (!has_proto_QUES(ist, parms[1], String_OBJ)) {
> + raise_exception(ist, OBJ(TYPE_EXC), "split function parameter 1
> must be a string"); + return NULL;
> + }
> +
> + find_len = pr_strlen(parms[1]);
> + if (find_len<1) {
> + raise_exception(ist, OBJ(TYPE_EXC), "split: empty separator");
> + return NULL;
> + }
> + src_ptr = pr_strptr(self);
> + where_ptr = src_ptr;
> + find_ptr = pr_strptr(parms[1]);
> +
> + temp_len = str_len;
> + a_match = bin_strstr(src_ptr,temp_len,find_ptr,find_len);
> + while (a_match) {
> + // Make new string from where_ptr to a_match.
> + obj_p next_frag = new_string_n_obj(ist,where_ptr,a_match-where_ptr);
> + list_append(ist,list_obj,next_frag);
> +
> + a_match += find_len; // Skip the matching text.
> + where_ptr = a_match;
> + temp_len = str_len - (a_match - src_ptr);
> + a_match = bin_strstr(a_match,temp_len,find_ptr,find_len);
> + }
> +
> + // Copy any trailing text after the last match.
> + obj_p next_frag =
> new_string_n_obj(ist,where_ptr,str_len-(where_ptr-src_ptr)); +
> list_append(ist,list_obj,next_frag); +
> + return list_obj;
> +}
> +
>  //********************************* STRINGGEN MODULE
> **************************
>
>  MODULE_START(StringGen)
> @@ -435,8 +741,6 @@
>          return res;
>  }
>
> -
> -
>  //********************************* MODULES INIT
> ******************************
>
>  MAIN_MODULE_INIT(String)
> @@ -461,6 +765,16 @@
>   MODULE_ADD_SYM(String, __delItem__);
>   MODULE_ADD_SYM(String, __objList__);
>   MODULE_ADD_SYM(String, __cDataLen__);
> + MODULE_ADD_SYM(String, lower);
> + MODULE_ADD_SYM(String, upper);
> + MODULE_ADD_SYM(String, replace);
> + MODULE_ADD_SYM(String, capitalize);
> + MODULE_ADD_SYM(String, expandtabs);
> + MODULE_ADD_SYM(String, lstrip);
> + MODULE_ADD_SYM(String, rstrip);
> + MODULE_ADD_SYM(String, strip);
> + MODULE_ADD_SYM(String, split);
> + MODULE_ADD_SYM(String, find);
>
>   MODULE_SUB_INIT(StringGen);
>   MODULE_ADD_SYM(StringGen, next);
>
> --
> Resist the feed.