Vala: warning and segmentation fault with to_utf32_fast method - c

I got a warning and a segmentation fault when compiling and running roughly the following code, which uses the to_utf32_fast method on a string and should return the number of UTF-32 characters encoded in the codepoint_count variable. This method transpiles to the C function g_utf8_to_ucs4_fast and somehow out codepoint_count ends up as long * * argument instead of the expected long *.
I do have a workaround, so this isn't urgent.
int main (string[] args) {
string test = "abc";
long codepoint_count;
string utf32_version = test.to_utf32_fast(-1, out codepoint_count).to_string();
stdout.printf("success");
return 0;
}
The relevant part of the output:
C:/msys64/usr/src/outerror.vala.c: In function '_vala_main':
C:/msys64/usr/src/outerror.vala.c:78:50: warning: passing argument 3 of 'g_utf8_to_ucs4_fast' from incompatible pointer type [-Wincompatible-pointer-types]
_tmp2_ = g_utf8_to_ucs4_fast (test, (glong) -1, &_tmp1_);
^
In file included from C:/msys64/mingw64/include/glib-2.0/glib/gstring.h:33:0,
from C:/msys64/mingw64/include/glib-2.0/glib/giochannel.h:34,
from C:/msys64/mingw64/include/glib-2.0/glib.h:54,
from C:/msys64/usr/src/outerror.vala.c:5:
C:/msys64/mingw64/include/glib-2.0/glib/gunicode.h:798:12: note: expected 'glong * {aka long int *}' but argument is of type 'glong ** {aka long int **}'
gunichar * g_utf8_to_ucs4_fast (const gchar *str,
^~~~~~~~~~~~~~~~~~~
I looked at the transpiled C source code and the third argument to g_utf8_to_ucs4_fast is the address of an uninitialized pointer to long which is later freed with g_free. This triggers a segfault when the program is run.
Am I doing something wrong in the way I'm calling this function? It's declared as public string32 to_utf32_fast (long len = -1, out long? items_written = null).
I'm new to Vala (more familiar with C) and not sure I grasp the argument annotations. The second argument shouldn't be transpiled to C as long ** rather than long *, but maybe the nullability marker ? or the default value = null leads Vala to think that the variable items_written is a pointer to long (or the Vala equivalent thereof) rather than long. If so, then maybe there is an error in the declaration of the method or an ambiguity in Vala syntax.

The declaration is wrong. This code works just fine:
[CCode (cname = "g_utf8_to_ucs4_fast")]
public extern string32 to_utf32_fast_ (string s, long len = -1,
out long items_written);
int main (string[] args) {
string test = "abc";
long codepoint_count;
string32 utf32_version = to_utf32_fast_(test, -1, out codepoint_count);
stdout.printf("success");
return 0;
}
In the original declaration from glib-2.0.vapi the items_written parameter would be a glong** in C, but it actually is a glong*.
I have reported this as a bug:
https://gitlab.gnome.org/GNOME/vala/issues/634

Related

Why is it when I return a `double` from a `void *` function, it becomes incompatible?

I am making a generic programming library, used to make my life a little bit easier when programming in C. The problem I ran across was that when returning a double from a void * function, it results in an incompatible types error. Why is that? I thought void * was supposed to return any datatype, and not just select ones?
main.c
void * _example_code();
int main(void) {
double res = _example_code();
}
void * _example_code() {
return 2.23;
}
The only things automatically convertible to void * are pointers to object types without qualifiers and null pointer constants, per C 2018 6.5.16.1 1.
double is not automatically convertible to void *.
You could return a pointer to double for a void * function, but you would need a double with lifetime extending beyond the function return, and there may be additional issues trying to implement a “generic” function this way. For one thing, when you return a double * or int * or other pointer type for a void *, the calling routine is not informed what type the pointer was before the conversion to void *. You need to write your program to handle that yourself, by providing some mechanism to inform the caller and have the caller convert the void * to the appropriate type.
There is no implicit conversion to void* from double. Such implicit conversions exist only for object pointer types and null pointer constants.
I wonder what you're expecting that code to do. If there were an implicit conversion, it would most likely take the bits of the double value 2.23 and interpret them as a pointer value of type void* (that's how explicit integer-to-pointer conversions typically work). The result would be garbage. In particular, it would not be a pointer to a memory location containing 2.23.
(The language doesn't say much about the results of integer-to-pointer or pointer-to-integer conversions. There's no guarantee that they just reinterpret the bits; that's just the most common implementation. Avoid converting between integers and pointers until and unless you really need to and know what you're doing. And conversions between pointers and floating-point types are not permitted at all.)
The error message I get for your code is:
c.c: In function ‘main’:
c.c:4:18: error: incompatible types when initializing type ‘double’ using type ‘void *’
4 | double res = _example_code();
| ^~~~~~~~~~~~~
c.c: In function ‘_example_code’:
c.c:8:12: error: incompatible types when returning type ‘double’ but ‘void *’ was expected
8 | return 2.23;
| ^~~~
The error message is actually a bit misleading. It's certainly true that void* and double are incompatible, but compatibility is not required here. For example, int and long are incompatible types, but you can legally return an int value from a function returning long, and the value will be implicitly converted. Type compatibility, as defined by the C standard, is a stronger condition that implicit convertibility. To a first approximation, a type is only compatible with itself (though there are other cases).
I've submitted a gcc bug report for the misleading error message:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107091
clang has a similar issue:
https://github.com/llvm/llvm-project/issues/58066
(Of course the code is still in error.)
C does not have built-in generic containers, void * is a generic data pointer: you can store the value of a pointer to any data type into it and get back the original pointer by converting it back to the original type.
The effect of storing a double into a void * is not defined by the C Standard and your compiler complains that it is an invalid conversion.
For a specified list of types of objects, say int, double and const char *, you can define a generic container this way:
#include <stdio.h>
typedef struct generic {
enum { INT, DOUBLE, STRING } type;
union {
int i;
double d;
const char *s;
} u;
} generic;
generic function1(void);
generic function2(void);
generic function3(void);
int print_generic(const char *s, generic v);
int main() {
generic v1 = function1();
generic v2 = function2();
generic v3 = function3();
print_generic("v1: ", v1);
print_generic("v2: ", v2);
print_generic("v3: ", v3);
return 0;
}
generic function1(void) {
return (generic){ DOUBLE, { .d = 2.23 }};
}
generic function2(void) {
return (generic){ INT, { .i = 42 }};
}
generic function3(void) {
return (generic){ STRING, { .s = "Hello" }};
}
int print_generic(const char *s, generic v) {
switch (+v.type) {
case INT: return printf("%s%d\n", s, v.u.i);
case DOUBLE: return printf("%s%g\n", s, v.u.d);
case STRING: return printf("%s%s\n", s, v.u.s);
default: return printf("%s%s\n", s, "unknown");
}
}
Also note that _example_code is a reserved identifier. Do not start you identifiers with an underscore, these are reserved for the compiler's internal needs.

Error when Compiling CPython: C2440 'function': cannot convert from 'PyObject' to PyObject *'

I'm new to using CPython (and C as well) and I was trying to create an extension module to calculate nCk. However, I'm coming across this really annoying error when compiling. Here's my code:
PyObject* combination(PyObject* n, PyObject* k){
PyObject* output = ONE;
return PyNumber_TrueDivide(factorial(n), PyNumber_Multiply(factorial(k), factorial(PyNumber_Subtract(n, k))));
}
static PyObject* BezierCurve_combination(PyObject* self, PyObject* args){
PyObject n, k;
if (!PyArg_ParseTuple(args, "OO", &n, &k))
return NULL;
/* This return statement is line 60*/
return Py_BuildValue("O", combination(n, k));
}
And here is the error message:
(60): error C2440: 'function': cannot convert from 'PyObject' to 'PyObject *'
(60): warning C4024: 'combination': different types for formal and actual parameter 1
(60): warning C4024: 'combination': different types for formal and actual parameter 2
What is causing this error? A similar program works really well for a factorial function definition (it contains just 1 PyObject* argument.) How can I fix this?
You can never have a PyObject in your program like that. They're always pointers to PyObjects.
Use PyObject *n, *k; - or, if in doubt,
PyObject *n;
PyObject *k;
Furthermore, you must always check the return value of each and every function in a Python exception, excepting if you're just returning it from your function. Thus you may not write
return PyNumber_TrueDivide(factorial(n),
PyNumber_Multiply(factorial(k), factorial(PyNumber_Subtract(n, k))));
You can only write
return PyNumber_TrueDivide(dividend, divisor);
For the inner calls you must check that they're not NULL separately, and if they are it signifies an exception that you must handle, or throw out (by returning NULL from your function).

C - Passing string in a function in a thread and returning it results in a non-existant, non-printable string?

Inside a thread, I run this function:
char *r = NetString("ch","aaaaa");
printf("%s",r); printf("%s","\n");
Which calls
char *NetString(char *id, char *data) {
char *result;
result = (char *)malloc(strlen(id)+strlen(data)+2);
strcpy(result, id);
strcat(result, "/");
strcat(result, data);
printf("%s",result); printf("%s","\n");
return (char *)result;
}
In the console, when I compile this warning shows up:
warning: initialization makes pointer from integer without a cast
[enabled by default]: char *r = NetString("ch","aaaaa");
^
If you need the full code (which may be a bit unorganized), here it is:
pastebin
Apparently, this code runs as expected (returns "ch/aaaaa" twice) when not run in a thread, but when I run it in a thread, the string only prints once (that is, in the NetString function). Any help? Thanks a lot.
This code
char *r = NetString("ch","aaaaa");
and this error
warning: initialization makes pointer from integer without a cast
[enabled by default]: char *r = NetString("ch","aaaaa");
^
almost certainly mean:
You are calling NetString() without providing a function prototype, thus it is treated as returning int.
You are running on a 64-bit platform where pointers are 64 bit and int values are 32 bit.
Thus, your char * pointer value from NetString() gets truncated to an int, then assigned to a pointer, where it no longer points to anything useful.
You need to provide a proper prototype with a function declaration:
char *NetString( char *, char * );
in all code that calls NetString(), and the prototype needs to be before any such calls.

Why does passing arrays of non-const members to functions receiving them as const generate compiler warnings? [duplicate]

The const modifier in C++ before star means that using this pointer the value pointed at cannot be changed, while the pointer itself can be made to point something else. In the below
void justloadme(const int **ptr)
{
*ptr = new int[5];
}
int main()
{
int *ptr = NULL;
justloadme(&ptr);
}
justloadme function should not be allowed to edit the integer values (if any) pointed by the passed param, while it can edit the int* value (since the const is not after the first star), but still why do I get a compiler error in both GCC and VC++?
GCC: error: invalid conversion from int** to const int**
VC++: error C2664: 'justloadme' : cannot convert parameter 1 from 'int **' to 'const int **'. Conversion loses qualifiers
Why does it say that the conversion loses qualifiers? Isn't it gaining the const qualifier? Moreover, isn't it similar to strlen(const char*) where we pass a non-const char*
As most times, the compiler is right and intuition wrong. The problem is that if that particular assignment was allowed you could break const-correctness in your program:
const int constant = 10;
int *modifier = 0;
const int ** const_breaker = &modifier; // [*] this is equivalent to your code
*const_breaker = & constant; // no problem, const_breaker points to
// pointer to a constant integer, but...
// we are actually doing: modifer = &constant!!!
*modifier = 5; // ouch!! we are modifying a constant!!!
The line marked with [*] is the culprit for that violation, and is disallowed for that particular reason. The language allows adding const to the last level but not the first:
int * const * correct = &modifier; // ok, this does not break correctness of the code

Converting string to long using strtol and pointers

My goal is to convert a string such as "A1234" to a long with value 1234. My first step was to just convert "1234" to a long, and that works as expected:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char* test = "1234";
long val = strtol(test,NULL,10);
char output[20];
sprintf(output,"Value: %Ld",val);
printf("%s\r\n",output);
return 0;
}
Now I am having trouble with pointers and trying to ignore the A at the beginning of the string. I have tried char* test = "A1234"; long val = strtol(test[1],NULL,10); however that crashes the program.
How do I set this up properly to get it pointing to the correct spot?
You are almost right. You need to pass a pointer to strtol, though:
long val = strtol(&test[1], NULL, 10);
or
long val = strtol(test + 1, NULL, 10);
Turning on some compiler warning flags would have told you your problem. For example, from clang (even with no special flags added):
example.c:6:23: warning: incompatible integer to pointer conversion passing
'char' to parameter of type 'const char *'; take the address with &
[-Wint-conversion]
long val = strtol(test[1],NULL,10);
^~~~~~~
&
/usr/include/stdlib.h:181:26: note: passing argument to parameter here
long strtol(const char *, char **, int);
^
1 warning generated.
and from GCC:
example.c: In function ‘main’:
example.c:6: warning: passing argument 1 of ‘strtol’ makes pointer from integer
without a cast
Editorial note: I think you can see from these error messages why beginners are often well-advised to use clang rather than GCC.

Resources