Inline Native, Native Properties, and Macro Methods
I've made a couple of additional improvements to Rogue's native support.
First I came up with an even better way to inline native code. You write
native("...")->Type
(the return type and parentheses are optional)
and the compiler not only throws the string into the output C++ code with
$marker
replacement, it also knows what type of value the native expression results in, allowing native expressions to interoperate easily with other Rogue code.
For example, here's a method that would return the system time in seconds for a version of Rogue that targeted Java:
class Time
ROUTINE
routine current->Real
return native( "System.currentTimeMillis()" )->Long / 1000.0
endClass
This would produce the following line of Java code:
return System.currentTimeMillis() / 1000.0;
The only minor problem is that this form of inline native (native code defined in line) is ambiguous with my inlineNative
keyword (native code defined separately that is inserted inline). I've consequently changed my existing inline
and inlineNative
keywords to be macro
and macro native
instead.
As an example of how things stand, here's a test class that produces the same line of code four times in four different ways:
# Test.rogue
Test()
class Test
METHODS
method init
local x = 5
print_test( x, x+1 )
print_test( x, native("($x + 1)")->Integer )
print_test( x, inline_plus_1(x) )
print_test( x, inline_native_plus_1(x) )
method print_test( a:Integer, b:Integer )
println "$ + 1 is $" (a,b)
method inline_plus_1( x:Integer )->Integer
macro x+1
method inline_native_plus_1( x:Integer )->Integer
macro native "($x + 1)"
endClass
# Output (roguec Test.rogue --execute)
5 + 1 is 6
5 + 1 is 6
5 + 1 is 6
5 + 1 is 6
# Test.init() in Test.cpp
RogueClassTest* RogueTest__init( RogueClassTest* THIS )
{
RogueInteger x_0 = (5);
RogueTest__print_test( THIS, x_0, (x_0 + 1) );
RogueTest__print_test( THIS, x_0, (x_0 + 1) );
RogueTest__print_test( THIS, x_0, (x_0 + 1) );
RogueTest__print_test( THIS, x_0, (x_0 + 1) );
return (RogueClassTest*)(THIS);
}
The second new native feature is the ability to add native properties to objects. These are really the simplest possible thing: just strings that get stuck into the class definition on the native side. But they allow Rogue-side objects to have C++ properties. For example, here's my revamped FileReader class. It used to be a [native]
class with a bunch of [native]
methods that mapped to a hard-coded definition in my C++ library, but now it's all defined Rogue-side:
class FileReader : Reader<<Character>>
PROPERTIES
filepath : String
buffer_count : Integer
buffer_position : Integer
count : Integer
position : Integer
native "FILE* fp;"
native "unsigned char buffer[1024];"
METHODS
method init( _filepath:String )
open( _filepath )
method close->this
native @|if ($this->fp)
|{
| fclose( $this->fp );
| $this->fp = 0;
|}
position = 0
count = 0
return this
method has_another->Logical
return (position < count)
method open( filepath )->Logical
close
native @|char path[ PATH_MAX ];
|$filepath->to_c_string( path, PATH_MAX );
|
|$this->fp = fopen( path, "rb" );
|if ($this->fp)
|{
| fseek( $this->fp, 0, SEEK_END );
| $count = (RogueInteger) ftell( $this->fp );
| fseek( $this->fp, 0, SEEK_SET );
|}
# Always close after the last byte is read
if (count == 0) close
return (position < count)
method peek->Character
if (position == count) return 0
if (buffer_position == buffer_count)
native @|$buffer_count = (RogueInteger) fread( $this->buffer, 1, sizeof($this->buffer), $this->fp );
buffer_position = 0
endIf
native @|return $this->buffer[ $buffer_position ];
method read->Character
if (position == count) return 0
local result = peek
++position
++buffer_position
if (position == count) close
return result
method remaining->Integer
return count - position
method set_position( @position )->this
native @|if ($this->fp)
|{
| fseek( $this->fp, $position, SEEK_SET );
|}
buffer_position = 0
buffer_count = 0
return this
endClass