Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Needs a parse tree for C files #1168

Open
tribusonz-2 opened this issue Sep 2, 2024 · 2 comments
Open

Needs a parse tree for C files #1168

tribusonz-2 opened this issue Sep 2, 2024 · 2 comments

Comments

@tribusonz-2
Copy link

In the current version of RDoc, when searching for class definition APIs such as rb_define_class and rb_define_method in a C file, if a match is found in that file, it seems to only accept the class and method definitions in that file.
This means that no matter how the method is defined in other C files, it will be ignored.

A mechanism will needed that to first gather elements from the C file and determine whether they are properly defined as classes. In this case, the parse tree would be desirable.
rb_xYYY variables used as object entities are basically the global variables, so they must be declared as extern when defining classes at the C level. However, this way does not work in outer method definitions (#pack/#unpack, etc.) because these "global variables" are not defined as classes.

In C, global variables must be initialized. A popular solution is to do the initialization in a single C file.

// global.h
#ifdef GLOBAL_VARIABLE_DEFINE
#define GLOBAL
#define GLOBAL_VAL(v) = (v)
#else
#define GLOBAL extern
#define GLOBAL_VAL(v)
#endif

This allows you to initialize variables as needed.

#define GLOBAL_VARIABLE_DEFINE // without extern
#include "global.h"

In Ruby, C's global variables are definitions, which means they have a different meaning than initialization.
Now below, definition in single file, RDoc is well parse here:

/*
 * call-seq:
 *   bar -> nil
 *
 * It is a test.
 */
static VALUE
foo_bar(VALUE self)
{
	return Qnil;
}

void
Init_foo()
{
	rb_cFoo = rb_define_class("Foo", rb_cObject);

	rb_define_method(rb_cFoo, "bar", foo_bar, 0);
}

However, a library that could become a small framework (such as image processing) is unlikely to be a single C file.
That is, it needs to be improved.

Below is a typical way I write it, and it is an example of an extension library that uses Professor Oura's FFT in Ruby.
The compiler passes it, but RDoc doesn't.

// ---(ruby/ext_extern.h)
#ifndef RUBY_EXT_EXTERN_H_INCLUDED
#define RUBY_EXT_EXTERN_H_INCLUDED

#if defined(__cplusplus)
extern "C" {
#endif

#ifdef     USE_GLOBAL_VARIABLE
# define   RUBY_EXT_EXTERN
#else
# define   RUBY_EXT_EXTERN    extern
#endif

#if defined(__cplusplus)
}
#endif

#endif /* RUBY_EXT_EXTERN_H_INCLUDED */
// ---

// ---(ruby/ooura_fft/globals.h)
#ifndef RUBY_OOURAFFT_GLOBALS_H_INCLUDED
#define RUBY_OOURAFFT_GLOBALS_H_INCLUDED

#include <ruby/internal/value.h> // VALUE
#include "ruby/ext_extern.h"

RUBY_EXT_EXTERN VALUE rb_mOouraFFT;

#endif /* RUBY_OOURAFFT_GLOBALS_H_INCLUDED */
// ---

// --- (ooura_fft.c)
#include <ruby.h>
#define  USE_GLOBAL_VARIABLE
#include "ruby/ooura_fft/globals.h"

void
Init_ooura_fft(void)
{
	rb_mOouraFFT = rb_define_module("OouraFFT");
	
	InitVM(FFT);
}
// ---

// --- (fft.c)
#include <ruby.h>
#include "ruby/ooura_fft/globals.h"

// :
// :

static void InitVM_FFTMain(void);
void
InitVM_FFT(void)
{
	InitVM(FFTMain);
}

// :
// :

static void
InitVM_FFTMain(void)
{
	rb_define_module_function(rb_mOouraFFT, "cdft", fft_cdft, -1);
	rb_define_module_function(rb_mOouraFFT, "rdft", fft_rdft, -1);
	rb_define_module_function(rb_mOouraFFT, "ddct", fft_ddct, -1);
	rb_define_module_function(rb_mOouraFFT, "ddst", fft_ddst, -1);
	rb_define_module_function(rb_mOouraFFT, "dfct", fft_dfct, -1);
	rb_define_module_function(rb_mOouraFFT, "dfst", fft_dfst, -1);
	rb_define_const(rb_mOouraFFT, "USING_THREAD", rb_str_new_cstr((const char *)USING_THREAD));
}
// ---

It is expected that more extension libraries with this syntax will be available in the future. You may want to reconsider RDoc as well.

@tribusonz-2
Copy link
Author

Additional information.

It is expected that more extension libraries with this syntax will be available in the future.

I stated this definitively, but this is the only syntax that will work. It's also a "de facto bug fix."

@tribusonz-2
Copy link
Author

The RDoc parser only checks words, not C syntax.
Therefore, it seems possible to write code that is passed by the compiler but ignored by the RDoc parser.
If it's readable, it's a bit "fashionable".

A sample is shown below:

// (foo.c)
#define RDocDummy 0

void
Init_foo(void)
{
	rb_cFoo = rb_define_class("Foo", rb_cObject);
	
#if RDocDummy
	rb_define_method(rb_cFoo, "bar", foo_bar, 0); // in define.c
#else
	InitVM(Define);
#endif
}
// (define.c)

/*
 * call-seq:
 *   bar -> String
 * 
 * This is a test.
 */
static VALUE
foo_bar(VALUE self)
{
	return rb_str_new_cstr("hello!!");
}

void
InitVM_Define(void)
{
	rb_define_method(rb_cFoo, "bar", foo_bar, 0);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant