Just plain old C++ RAII (or C# using
declaration) implementation for TypeScipt via 'babel-plugin-macros'
.
Suppose you have a local resource handle that you must manually release in the same scope.
With this plugin the cleanup is done automatically for you.
import using from 'using.macro';
const resource = using (new Resource());
using (new AutoVarResource());
doStuffWithResource(resource);
↓ ↓ ↓ ↓ ↓ ↓
const resource = new Resource();
try {
const _handle = new AutoVarResource(); // variable is created for you
try {
doStuffWithResource(resource);
} finally {
_handle.delete(); // this part is fully customizable!
}
} finally {
resource.delete(); // this part is fully customizable!
}
Make sure babel-plugin-macros
,
is added as "macros"
item in "plugins"
array of your babel config file.
Afterwards just install this package as dev dependency and use it in your code:
npm i -D using.macro
The way you import this macro doesn't matter (as long as TypeScript eats it):
import using from 'using.macro';
import kek from 'using.macro';
import { using } from 'using.macro';
import { using as kek } from 'using.macro';
There are only two options for invoking this macro:
- Near variable declaration
var/let/const varId = using(expresssion);
- As a single expression statement
using(expression);
so that a variable will be created automatically to release it for you.
By default when you do
import using from 'using.macro';
you get .delete()
method called on your handles.
If you want to change this behaviour to e.g. call free()
function on your handle,
you can wrap this macro with your own via createUsingMacro()
from 'using.macro/create'
:
// using-ptr.macro.ts # .macro extension matters! See babel-plugin-macros docs
import { createMacro } from 'babel-plugin-macros';
import { createUsingMacro, createFunctionCallFactory } from 'using.macro/create';
export default createMacro<Fn>(createUsingMacro(createFunctionCallFactory('free')));
// Pass this noop function type to createMacro<>() for TypeScript support.
type Fn = <T>(val: T) => T;
and use it in your code this way:
import usingPtr from './using-ptr.macro';
const ptr = usingPtr (malloc(42));
// stuff
↓ ↓ ↓ ↓ ↓ ↓
const ptr = malloc(42);
try {
// stuff
} finally {
free(ptr);
}
create[Function/Method]CallFactory()
are simply two helper
functions. You can actually pass your own destructor codegen function instead:
import * as T from '@babel/types'; // default import is not supported by babel!
createUsingMacro(garbageVariableIdentifier => {
// you may return a simple expression or a statement or an array of statements here
return T.callExpression(T.identifier('free'), [garbageVariableIdentifier.node]);
})
Unfortunately 'babel-plugin-macros'
doesn't natively support TypeScript macros
in runtime, so you need to build your custom *.macro.ts
to *.macro.js
file,
and put in the same directory as ts
file.
If you are looking for a native using
binding syntax, please support this stage 2 proposal and upvote
this proposition to it.
This macro was originally created for simplifying the usage of
'embind'
C++ object
handles