Native Methods in Rogue

With Rogue I'm achieving a long-standing dream of being able to embed native method implementations within Rogue source in a simple way. There are now several ways to hook in native code; I'll briefly touch on them all using a series of examples.

Rogue's primitives types (Real, Float, Long, Integer, Character, Byte, Logical) are half hard-coded into the compiler and half defined in the Standard Library. The standard library defines a class definition for each primitive type which adds a number of methods. For example, class Integer could define a method that retrieves a specific digit at a zero-based index (0 = ones digit) of the context integer:

class Integer [primitive]
  METHODS
    method at( index:Integer )->Integer
      local n = this
      forEach (1..index) n /= 10
      return n % 10

    # ...
endClass

# ...
println 456.at(0)  # prints: 6
println 456.at(1)  # prints: 5
println 456.at(2)  # prints: 4

Now let's add a method to class Real to return the floor of a number. We'll hook into native C++ (Rogue's default compile target) to handle it using the inlineNative statement/method type:

class Real [primitive]
  METHODS
    method floor->Real
      inlineNative "floor($this)"
endClass

Here's some sample Rogue code that uses it:

local x = 3.2
local y = 3.9
println x.floor + y.floor

And here's the output C++ that the Rogue compiler would generate:

RogueReal x_0 = (3.2);
RogueReal y_1 = (3.9);
RogueGlobal__println(
  ((RogueClassGlobal*)ROGUE_SINGLETON(Global)),
  (floor(x_0) + floor(y_1))
);

Now let's add a native Real as Long (aka toLongBits) method. nativeInline is only suitable for a native code consisting of a single expression, so let's use a [native] method attribute instead:

class Real [primitive]
  METHODS
    method as_long->Long [native]
endClass

When we compile the above, this code:

println pi.as_long

Becomes this C++ code:

RogueGlobal__println(
  ((RogueClassGlobal*)ROGUE_SINGLETON(Global)),
  (RogueReal__as_long( Rogue_program.pi ))
);

Compiling the C++ code will throw an error unless we add in a C++ definition of RogueReal__as_long(RogueReal) to the C++ mix:

RogueReal__as_long( RogueReal value )
{
  return *((RogueLong*)(&value));
}

That works fine and all but now there's an even simpler way: using an embedded native block. Using the keyword native in a method body works similar to inlineNative except that the compiler framework generates the C++ "wrapper" as well - and you can also mix regular Rogue statements and embedded native blocks if you know what you're doing:

class Real [primitive]
  METHODS
    method as_long->Long
      native "return *((RogueLong*)(&$this));"
endClass

This one stop shop produces the same result as the three-part native attribute, native function prototype, and native function definition that we wrote before.

Rogue's verbatim strings come in really handy here too. Here's Rogue's definition of Time.current (like Java's System.currentTimeMillis() but with the result in seconds instead of milliseconds):

class Time
  ROUTINES
    routine current->Real
      $if ("C++")
        native @|#if defined(_WIN32)
                |  struct __timeb64 time_struct;
                |  RogueReal time_seconds;
                |  _ftime64_s( &time_struct );
                |  time_seconds = (RogueReal) time_struct.time;
                |  time_seconds += time_struct.millitm / 1000.0;
                |  return time_seconds;
                |
                |#else
                |  struct timeval time_struct;
                |  RogueReal time_seconds;
                |  gettimeofday( &time_struct, 0 );
                |  time_seconds = (RogueReal) time_struct.tv_sec;
                |  time_seconds += (time_struct.tv_usec / 1000000.0);
                |  return time_seconds;
                |#endif
      $endIf
endClass