Is there any way to use GLIB with Boehm GC?

Hello.

I’m interested in using GLib with Boehm GC, I am not so familiar with the C lang, though.

I found the topic at StackOverFlow, but the topic is so old that the way shown there is already obsolete, and MemVTable is deprecated.
Is there any new alternative way to use GLib with Boehm GC? I wish to write a Lisp implementation, GLisp maybe?, for my practice and fun.

Thanks.

This is a really interesting question. It is definitely not supported out of the box. As you say, it might have been possible with GMemVTable, but that was removed IIRC for performance reasons and for avoiding a combinatorial explosion of test conditions.

If you want to do this, I’d suggest compiling your own copy of GLib with g_new/g_malloc/g_free and friends rewritten to use the Boehm library.

Not really. Custom GMemVTables were removed because GLib switched to using constructor functions to initialise the library on load, and that is fundamentally incompatible with changing the implementation of the allocator functions.

Thank you, both guys.

Anyway, I understand it is impossible to use Boehm GC with GLib, unless I dig into the source code level of GLib.
By the way, I found this article, and it implies g_autoptr woks as well as a garbage collector and we do not requre garbage collectors externally, I could not find anything around g_autoptr stuff on the GLIB documentation, though…
Am I understanding it correctly?

Thanks.

g_autoptr is a wrapper around GCC’s cleanup attribute, with some pre-processor macro for types to automatically declare a cleanup function for that type.

Basically, you use it to declare a variable that will be “cleaned up” once it goes out of scope:

{
  g_autoptr (SomeType) s = get_some_value ();

  // use s as normal, until the scope ends, and the
  // variable gets collected for cleanup
}

The cleanup attribute is implemented only by GCC and clang, which means g_autoptr (and g_auto, and g_autofree) is not portable across toolchains.

The various macros and their behaviours are documented in the GLib reference.

Hello. Mr. ebassi.

This means we cannot use g_autoptr with, for instance, MSVC, even though I do not know whether MSVC can handle GLib or not.
Do not worry. I am a Linux user.
(I have once googled whether we could use GLib on Windows; however what I found is the possibilily to use GLib with mingw.)

Anyway, I would check GCC’s cleanup attribute out, because I would like to know what would be happened if we designed a function using g_autoptr and returning a value, that must be the s in your example, and if we assign the value to a variable in another function. In other words, I would like to know what out of scope really means.

Thanks!

GLib supports MSVC, yes; native Windows builds using MSVC are part of our CI pipeline and testing infrastructure. This is why GLib itself does not use g_autoptr and friends internally.

That’s defined by the C standard, specifically paragraph 2 of § 6.2.1:

For each different entity that an identifier designates, the identifier is visible (i.e., can be used) only within a region of program text called its scope. Different entities designated by the same identifier either have different scopes, or are in different name spaces. There are four kinds of scopes: function, file, block, and function prototype. (A function prototype is a declaration of a function that declares the types of its parameters.)

In other words, the cleanup function for s in the example above is called as soon as the control flow exits the { } block. This means that if you pass the s pointer to something else, you’ll likely handing over invalid memory. For that reason GLib also provides g_steal_pointer(), which returns the contents of the pointer and sets it to NULL at the same time, essentially implementing “move” semantics; for instance:

{
  g_autoptr (SomeResult) r = perform_some_action ();

  // Transfer the result if it's valid...
  if (some_result_is_valid (r))
    do_something_with_result (g_steal_pointer (&r));

  // otherwise clean it up automatically
}

Hello, again.

I see.
I mean I have only found an article around minGW with GLib. Nobody says around GLib with MSVC surprisingly.

Yes, I understand the meaning of scope itself, because Lisp’s lots of irritating superfluous parenthesis represent scopes; in other word, it is lots of irritating scoping processor.
To be honest, because of daily heavily dependence on the language with GC, it is just hard to imagine at what timing a malloc-ed object must be freed. I am trying to demonstrate to write a code, but it is tough now.

typedef struct {
  guint32 secret_number;
  gint guess;
} Env_t;

This is O.K.

Env_t* init() {
  Env_t* env = g_new(Env_t, 1);
  env->secret_number = g_random_int_range(1, 101);
  env->guess = -1;
  return env;
}

This is O.K. but:

g_autoptr(Env_t) env = init();

GCC and Clang got mad at me.Oops!
It might take a while to understand what is going on and what is the right way to do.
It would take a time to learn to use g_autoptr.

By the way, I googled and found the term “smart pointers” about C++, but it may differ from what you explained. Articles say counting references, thus autoptr-like stuffs of C++ might have different mechanism if I understood correctly.

Thanks.

You need to:

  • define a cleanup function for the type
  • use G_DEFINE_AUTO_CLEANUP_FREE_FUNC

e.g.

typedef struct {
  guint32 secret_number;
  int guess;
} Env_t;

void
env_free (Env_t *ptr) {
  g_free (ptr);
}

Env_t *
env_new (void) {
  Env_t *res = g_new (Env_t, 1);

   // ...

   return res;
}

G_DEFINE_AUTO_CLEANUP_FREE_FUNC (Env_t, env_free)

in order to be able to use g_autoptr().

Yes, it does take time—mainly, you need to read the documentation first.

Smart pointers are another thing entirely.

GLib’s autoptr is just a way to bind a free function to a type; it does not have the same semantics of C++'s smart pointer types, like unique_ptr (which describe ownership); or shared_ptr (which describes strong and weak references).

Ah…I slowly understood how g_autoptr works.
Out of scope is, yes, simply out of scope. Nothing difficult, as the document states.

What I wondered was what would be happened if we made a function, having a g_autoptr-ed GSomething and returning it.
I understand functional programming seems inefficient from the C programming point of view, but anyway, the way is generating objects and discarding them, as a sort of chained sequence. That is why I was interested in what happens if a function returns g_autoptr-ed GSomething.
I expected g_autoptr with GSomething would help a sort of functional programming in the C language, but last time, no good examples popped up in my mind.

Here is a silly but simple example:

#include <glib-2.0/glib.h>
#include <stdio.h>

GString* number2string(gint z, gint radix) {
  g_autoptr(GString) a = g_string_new("");
  g_autoptr(GString) b = g_string_new("");
  while (z != 0) {
    g_string_printf(b, "%d", z % radix);
    g_string_prepend(a, b->str);
    z /= radix;
  }
  return a;
}

int main(void) {
  gchar base[3];
  scanf("%2s%*[^\n]", base);
  getchar();
  gchar num[20];
  while (scanf("%19s%*[^\n]", num) != EOF) {
    getchar();
    g_print("%s\n", number2string(strtol(num, NULL, 10), strtol(base, NULL, 10))->str);
  }
  return EXIT_SUCCESS;
}

This code tries converting from a number in decimal, inputted from the terminal, to its binary one. The number2string function, named after number->string function of the Scheme language, has two g_autoptr-ed GString objects, where a is finally returned but b is not.
Now what happens? Nothing printed.
number2string tries returning a GString object, or a, but miserably g_autoptr discards what a is because of “out of scope”. Simple.
Thus, if we want to make a GSomething returned, we should not make it sandwiched with g_autoptr. That is gonna be a kind of “know-how”.

Anyway, this experiment makes me clear.

Thanks.

You cannot return a here: the memory will be deallocated once the function returns, so you’re going to access garbage data.

Replace return a with return g_steal_pointer (&a), to ensure that a gets nullified (so it doesn’t get freed) by g_autoptr. Of course, you’ll also need to handle the returned GString in your main:

g_autoptr (GString) s = number2string(strtol(num, NULL, 10), strtol(base, NULL));
g_print ("%s\n", s->str);

As a side note, GString is a string buffer builder, not a string class; you should use it to build a string and then return the string, e.g.

char *
number2string (int z, int base) {
  GString *buf = g_string_new (NULL);
  // ...build the string...
  return g_string_free (buf, FALSE);
}

// ...

int
main (void) {
  // ...get the parameters...
  g_autofree char *s = number2string (num, base);
  g_print ("%s\n", s);
  // ...
}

Hello.

Thanks for your advice, and your advice made me noticed theoretically, yes, just theoretically, that we can write such a code:

#include <glib-2.0/glib.h>

int main(void) {
  g_print("%s\n", g_string_free(g_string_new("Hello, GLIB!"), FALSE));
  return EXIT_SUCCESS;
}

Yes, it is silly and nonsense, but we can.
I do not know whether this style is acceptable or not, but functional programming tries avoiding assignment as possible as it can.
I am kinda happy, but do not worry. This is just experimental.

Anyway, thank you!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.