Custom Data Types

Here’s something special!
For an upcoming rather large project I want to help myself making the Haxe code more readable.
Since I’m quite used to C data types I’d like to use those too. Unfortunately Haxe just offers me Int, UInt and Float. So I was thinking of using my own C-like data types and even throw an error if I accidentally assign a value that’s out of the range.
Let’s consider int8_t, ranging from -127 to 128.
So far I’ve tried defining an abstract in three ways.

A

/**
	signed char (-128 .. 127)
**/
abstract Int8(Int) {
  inline public function new(i:Int) {
	  if (i<-128 || i>127)
	  {
		throw("value exceeds range");
	  }
          this=i;
  }
}

B

/**
	signed char (-128 .. 127)
**/
abstract Int8(Int) from Int to Int {
}

C

/**
	signed char (-128 .. 127)
**/
abstract Int8(Int) {
    inline function new(i) this = i;
    @:from static function _(i:Int):Int8 {
	  if (i<-128 || i>127)
	  {
		throw("value exceeds range");
	  }
        return new Int8(i);
    }
}

With A, I would need to call the the constructor to get my range check.
So var a:Int8=new Int8(300); would throw an error (as desired) while var a:Int8=300; would work flawlessly.

Using the C approach, my error would be thrown in both cases (though the IDE points me to the throw message in the code, not where the error actually occured) but I’m afraid this method has it’s drawbacks.
Does it instantiate a new instance everytime I assign a different value to the variable?

Method B is just for reference - I won’t use it. :wink:

What would be the most elegant solution to achieve my goal?

Furthermore, this data types will be used all across my project - so many classes will be using it. How can I embedd the definition globally? It’s not like a class, is it?

Have you tried to declare the @:from method as inline? That might help with positions.

Each time you assign an Int, yeah. Perhaps it would make sense to guard the range check with #if debug, so you don’t have runtime overhead in actual release builds.

I’m not really sure what you mean by that… Abstracts are types, just like classes, interfaces, enums… You just import it as usual (or put an import into an import.hx, although if Int8 is in the root package it should just be available everywhere anyway)? :slight_smile:

Thanks Gama11! That did the trick! :smiley:

/**
	signed char (-128 .. 127)
**/
#if debug
	abstract Int8(Int)
#else
	abstract Int8(Int) from Int to Int
#end
{
	#if debug
	inline function new(i) this = i;
	@:from inline static function _(i:Int):Int8
	{
		if (i<-128 || i>127)
		{
			throw("value exceeds range");
		}
		return new Int8(i);
	}
	#end
}

By the way, I wasn’t aware that I can put a definition like the above in a file like e.g. DataTypes.hx and simply reference it by import DataTypes;. Funnily I stumbled over the exact same blog post you’ve mentioned - although putting everything into an import.hx file didn’t work. Do I need to do something special to make Haxe aware of that file?

You can only put imports and usings into a import.hx.

1 Like

Ah, that explains it. I thought it’s a general-purpose-stuff-in-what-you-desire file. Thanks once more!