In Haxe, how do you read a variable name inside of a Macro? - macros

I'm trying to use Macros to convert some variable declarations from this:
function test():Void {
var someComp:Component = __SOME_MACRO__();
// Or...
#getCompById var someComp:Component;
// Or even simpler...
getCompById(someComp, Component); //do some fancy macro magic...
// Also, if it's not possible/easy with a variable ...
getCompById("someComp", Component); //with a string of the variable name.
}
... to this:
function test() {
var someComp:Component = cast container.getCompById("someComp");
}
I'm leaning more toward the 3rd option (shorter syntax, same results).
But I have no idea how to write the macro (should it take a String as parameter? An expression?) and how to properly return that as a macro expression.
This is the (broken) code I've got so far:
macro static function getCompById(someVar:Expr, typeVar:Expr) {
return macro {
var someVar:typeVar = cast container.getCompById("someVar");
};
}
Any ideas?

The issue with the code you posted is first that you'd need reification escaping mechanisms for this to work correctly - so the first change would be to use the macro escapes:
return macro var $someVar:$typeVar = cast container.getCompById($v{someVar});
Now there will be some problems with this: It's expecting someVar to be of type String, and typeVar to be of type ComplexType. It's easy to get the string component from an Expr. It's not so easy however to transform an Expr into ComplexType. The easiest way to do that is to use the tink_macros library and use asComplexType
So the (untested) code will look something like:
using tink.MacroAPI;
using haxe.macro.Tools;
macro static function getCompById(someVarExpr:Expr, typeVarExpr:Expr)
{
var typeVar = typeVarExpr.toString().asComplexType();
switch (someVarExpr.getIdent())
{
case Success(someVar):
return macro var $someVar:$typeVar = cast container.getCompById($v{someVar});
case Failure(error): throw error;
}
}

Related

What is the best conversion from String into UnsafePointer<Int8>?

To convert String into UnsafePointer I use this:
var tail = ("" as NSString).utf8String
But is there any way to convert without NSString casting?
I use UnsafePointer inside C-library methods.
There's special method exactly for this: .withCString(_:):
yourString.withCString { pointer in
// work with the pointer
return result
}
Or if you want it as property there's .utf8CString:
var tail = "".utf8CString // ContiguousArray<CChar> (aka ContiguousArray<Int8>)
// and then
tail.withUnsafeBufferPointer { pointer in
// work with the pointer
return result
}

Can I use a macro to replace specific expressions within a block of code?

I am trying to write a set of Rust macros to execute certain code in a special way. Currently, I am writing it like this.
macro_rules! macro_stmt{
([$e:expr]) => {
// do something ...
}
}
and calling it
macro_stmt!([...]);
let var = macro_stmt!([...]);
macro_stmt!([...]);
// etc...
Which is great! Essentially, I am running an expression (the ellipsis are a placeholder for some unspecified code) inside the square brackets in a special way and the macro_stmt macro already works great.
What I would like to be able to do is write a macro so that I can run it like a block of code:
macro_rules! macro {
// some rule to call macro_stmt!
// this pattern isn't correct, BTW
($($line:stmt);+;) => {
$(macro_stmt($line);)+
}
}
and call it
macro! {
[...];
let var = [...];
[...];
// etc...
}
but the compiler yells at me for the stmt fragment not being correct syntax as the contents of the 'array' are not the correct comma-separated format.
Is there a way that I can call macro_stmt line-by-line without it checking syntax? The lines with no let var = would just call macro_stmt() but the lines with a let would prefix it with the same let var = macro_stmt()
The intended first expansion of macro would be
macro_stmt!([...]);
let var = macro_stmt!([...]);
macro_stmt!([...]);
Is this even possible?

Why doesn't this base type extension work?

Trying to play with extensions, but am having issues getting the following to work:
let value = -13
abs(value)
extension Int {
var abs:Int {
return abs(self) // -> Cannot invoke 'abs' with an argument list of type '(Int)'
}
}
value.abs
The compile error is weird, because it demonstrably runs the abs() function directly above with an Int as an argument. I've still got some light bulbs to trigger for generics I guess. Enlighten me.
The Swift compiler is confused that you use the abs variable as a function, which it cannot do. Now you could look at all the answers and rename your variable, but these do not give insight in how Swift functions work.
Swift automatically imports the Swift framework, where it defines its static functions. To use these functions, you usually do not need to specify that it's from the framework, but in cases like this, you should specify that you want to use the abs method from the Swift framework.
So after all the explanation, here's your code, which will work:
let value = -13
abs(value)
extension Int {
var abs: Int {
return Swift.abs(self)
}
}
value.abs
It appears just a call resolution problem. This will work:
let value = -13
abs(value)
extension Int {
var abs1:Int {
return abs(self)
}
}
value.abs1
And this will work too:
extension Int {
var abs:Int {
return self < 0 ? -self : self
}
}
value.abs
The problem here is that you are extending Int to add a variable named abs -- which is also the name of the function you are calling.
When you try to call the function abs() on the Int, it sees the variable abs that you created and it is confused because it thinks you are trying to return that variable and doesn't understand why you are sending it a parameter.
If you rename your variable to absoluteValue or anything else really, it should work.
let value = -13
abs(value)
extension Int {
var absoluteValue:Int {
return abs(self)
}
}
value.abs
Update: As others have stated, you can also solve the disambiguation of the use of abs by explicitly calling the function within the Swift framework. This should work just as well as the above solution.
let value = -13
abs(value)
extension Int {
var abs:Int {
return Swift.abs(self)
}
}
value.abs
Though, personally, I would still rename my new function to absoluteValue as in the first example so that its clear that you aren't calling the Swift.abs() when you use your abs variable.
Thanks to the direction of the original two answers (clash between global free function and the var I was defining), they have to be disambiguated. Rather than do my own inline implementation of abs or be forced to use a different name, I can properly scope the inside abs() using the Swift namespace.
extension Int {
var absoluteValue:Int {
return Swift.abs(self)
}
}
This gives me the best of both worlds (IMO).

Swift convert string to UnsafeMutablePointer<Int8>

I have a C function mapped to Swift defined as:
func swe_set_eph_path(path: UnsafeMutablePointer<Int8>) -> Void
I am trying to pass a path to the function and have tried:
var path = [Int8](count: 1024, repeatedValue: 0);
for i in 0...NSBundle.mainBundle().bundlePath.lengthOfBytesUsingEncoding(NSUTF16StringEncoding)-1
{
var range = i..<i+1
path[i] = String.toInt(NSBundle.mainBundle().bundlePath[range])
}
println("\(path)")
swe_set_ephe_path(&path)
but on the path[i] line I get the error:
'subscript' is unavailable: cannot subscript String with a range of
Int
swe_set_ephe_path(NSBundle.mainBundle().bundlePath)
nor
swe_set_ephe_path(&NSBundle.mainBundle().bundlePath)
don't work either
Besides not working, I feel there has got to be a better, less convoluted way of doing this. Previous answers on StackOverflow using CString don't seem to work anymore. Any suggestions?
Previous answers on StackOverflow using CString don't seem to work anymore
Nevertheless, UnsafePointer<Int8> is a C string. If your context absolutely requires an UnsafeMutablePointer, just coerce, like this:
let s = NSBundle.mainBundle().bundlePath
let cs = (s as NSString).UTF8String
var buffer = UnsafeMutablePointer<Int8>(cs)
swe_set_ephe_path(buffer)
Of course I don't have your swe_set_ephe_path, but it works fine in my testing when it is stubbed like this:
func swe_set_ephe_path(path: UnsafeMutablePointer<Int8>) {
println(String.fromCString(path))
}
In current version of Swift language you can do it like this (other answers are outdated):
let path = Bundle.main.bundlePath
let param = UnsafeMutablePointer<Int8>(mutating: (path as NSString).utf8String)
It’s actually extremely irritating of the library you’re using that it requires (in the C declaration) a char * path rather than const char * path. (this is assuming the function doesn’t mutate the input string – if it does, you’re in a whole different situation).
If it didn’t, the function would come over to Swift as:
// note, UnsafePointer not UnsafeMutablePointer
func swe_set_eph_path(path: UnsafePointer<Int8>) -> Void
and you could then rely on Swift’s implicit conversion:
let str = "blah"
swe_set_eph_path(str) // Swift implicitly converts Strings
// to const C strings when calling C funcs
But you can do an unsafe conversion quite easily, in combination with the withCString function:
str.withCString { cstr in
swe_set_eph_path(UnsafeMutablePointer(cstr))
}
I had a static library (someLibrary.a) written in C++ compiled for iOS.
The header file (someLibrary.h) had a function exposed like this:
extern long someFunction(char* aString);
The declaration in Swift looks like this:
Int someFunction(aString: UnsafeMutablePointer<Int8>)
I made an extension to String:
extension String {
var UTF8CString: UnsafeMutablePointer<Int8> {
return UnsafeMutablePointer((self as NSString).UTF8String)
}
}
So then I can call the method like so:
someFunction(mySwiftString.UTF8CString)
Update: Make String extension (swift 5.7)
extension String {
var UTF8CString: UnsafeMutablePointer<Int8> {
return UnsafeMutablePointer(mutating: (self as NSString).utf8String!)
}
}

Pattern variable binding cannot appear in an expression

I've been looking at The Swift Programming Language guide provided by apple.
Following sample is from the book:
class HTMLElement {
let name :String;
let text: String?;
#lazy var asHTML : () -> String = {
if let text = self.text {
return "<\(self.name)>\(self.text)</\(self.name)>";
} else {
return "<\(self.name) />"
}
}
}
I incorrectly wrote the closure as follow:
#lazy var asHTML : () -> String = {
if (let text = self.text) {
return "<\(self.name)>\(self.text)</\(self.name)>";
} else {
return "<\(self.name) />"
}
}
Notice the parentheses around let text = self.text and compiler complain about:
Pattern variable binding cannot appear in an expression
Just wondering what does Pattern Variable Binding mean, and why it cannot appear in an expression?
A "pattern variable binding" is the thing you're doing, i.e. using let in the middle of some code, not at the top level of a file or enum or struct or class as a way of declaring a constant variable.
What makes it an expression is the parentheses. You've cut the "let" expression off from its surroundings and asked for evaluation of it as an expression separately. But you can't do that: you can't say "let" just anywhere.
Another way of looking at it is simply this: if let is a fixed meaningful pattern, where the condition is an Optional being evaluated into a constant for use inside the if-code. The parenthesis broke up the pattern.
The pattern is called a binding because you're defining this name very temporarily and locally, i.e. solely down into the if-code. I think it goes back to LISP (at least, that's where I've used "let" this way in the past).