Getter functions for C strings
We have several functions that return C strings. For example, util_node_name
. Apart from the bad name (it should be renamed to something like fs_get_host_name
) the problem is that the user cannot check (without the string comparison and the knowledge of the implementation details of the function) whether the returned value is a real name or "unknown"
. There is also a redundant string copy. In general, such functions should be implemented as follows:
// fs_string_of_interest is known at compile time
static const char *const fs_string_of_interest =
#if defined(HAVE_STRING_OF_INTEREST)
"string of interest"
#else
NULL
#endif
;
void fs_get_string_of_interest(const char **const val, size_t *const val_len)
{
*val = fs_string_of_interest;
*val_len = (*val == NULL) ? 0 : strlen(*val);
}
// fs_another_string is generated at runtime but never changes (non-threadsafe)
#define ANOTHER_STRING_MAX_LENGTH 64
static char fs_another_string[ANOTHER_STRING_MAX_LENGTH] = { '\0' };
static int fs_generate_another_string()
{
return snprintf(fs_another_string, ANOTHER_STRING_MAX_LENGTH, "another string")
}
void fs_get_another_string(const char **const val, size_t *const val_len)
{
static int len = -1;
if (len < 0)
{
len = fs_generate_another_string();
}
*val = (len == 0) ? NULL : &fs_another_string[0];
*val_len = len;
}
MODULE fs_whatever
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_ASSOCIATED, C_F_POINTER, c_char, c_ptr, c_size_t
IMPLICIT NONE
PRIVATE
PUBLIC :: get_string_of_interest
PUBLIC :: get_another_string
CHARACTER(*), PARAMETER :: unknown_value = 'unknown'
CONTAINS
FUNCTION get_string_of_interest(fallback)
CHARACTER(:), ALLOCATABLE :: get_string_of_interest
CHARACTER(*), OPTIONAL, INTENT(in) :: fallback
INTERFACE
SUBROUTINE fs_get_string_of_interest(val, val_len) BIND(c)
IMPORT c_ptr, c_size_t
TYPE(c_ptr), INTENT(out) :: val
INTEGER(c_size_t), INTENT(out) :: val_len
END SUBROUTINE fs_get_string_of_interest
END INTERFACE
TYPE(c_ptr) :: c_val
INTEGER(c_size_t) :: c_val_len
CALL fs_get_string_of_interest(c_ver, c_ver_len)
IF (PRESENT(fallback)) THEN
get_string_of_interest = c_ptr_to_str(c_ver, c_ver_len, fallback)
ELSE
get_string_of_interest = c_ptr_to_str(c_ver, c_ver_len, unknown_value)
END IF
END FUNCTION get_string_of_interest
FUNCTION get_another_string(fallback)
! Almost identical to get_string_of_interest
END FUNCTION get_another_string
! The following functions should be made public functions of a separate module so they can be reused:
FUNCTION f_ptr_to_str(ptr)
CHARACTER(:), ALLOCATABLE :: f_ptr_to_str
CHARACTER(c_char), POINTER, INTENT(in) :: ptr(:)
INTEGER(c_size_t) :: ii
ALLOCATE (CHARACTER(SIZE(ptr)) :: f_ptr_to_str)
DO ii = 1, SIZE(ptr)
f_ptr_to_str(ii:ii) = ptr(ii)
END DO
END FUNCTION f_ptr_to_str
FUNCTION c_ptr_to_str(ptr, str_len, fallback)
CHARACTER(:), ALLOCATABLE :: c_ptr_to_str
TYPE(c_ptr), INTENT(in) :: ptr
INTEGER(c_size_t), INTENT(in) :: str_len
CHARACTER(*), INTENT(in) :: fallback
CHARACTER(c_char), POINTER :: f_ptr(:)
IF (C_ASSOCIATED(ptr)) THEN
CALL C_F_POINTER(ptr, f_ptr, [str_len])
c_ptr_to_str = f_ptr_to_str(f_ptr)
ELSE
c_ptr_to_str = fallback
END IF
END FUNCTION c_ptr_to_str
END MODULE whatever