Problem
Consider the following C#/C interop scenario:
public static extern IntPtr lua_newstate();
public static extern void lua_close(IntPtr state);
IntPtr luaState = lua_newstate();
// use lua ...
lua_close(luaState)
Obviously this works well enough, however there’s a fringe possibility that an IntPtr
not pointing to a lua state could be passed to lua_close
, since it accepts any IntPtr
.
I realized that it’s possible to use the behavior of struct marshaling to make this interop a bit more type-safe
[StructLayout(LayoutKind.Sequential)]
public struct LuaState
{
public readonly IntPtr Pointer;
}
public static extern LuaState lua_newstate();
public static extern void lua_close(LuaState state);
LuaState luaState = lua_newstate();
// use lua ...
lua_close(luaState)
Since LuaState
contains only an IntPtr
, the marshaler happily converts the native pointer returned by lua_newstate
to an instance of LuaState
, and will also quite happily convert a LuaState
struct to a native pointer when passed back to a native function.
This essentially creates a typed pointer. There is a cost associated with passing LuaState
though the Marshaling system instead of passing IntPtr
directly to the native side, but in testing the cost appears to be negligible.
Questions: Does using LuaState
to create a typed wrapper for IntPtr
in this way raise any red flags? Has anyone done something similar and run into issues?
Solution
I think, I would experiment with a safe or smart pointer approach in a way like this:
public sealed class LuaHandle : IDisposable
{
private static extern IntPtr lua_newstate();
private static extern void lua_close(IntPtr state);
private IntPtr m_handle = IntPtr.Zero;
public LuaHandle()
{
m_handle = lua_newstate();
}
public IntPtr Handle => m_handle;
public void Dispose()
{
if (m_handle != IntPtr.Zero)
{
lua_close(m_handle);
}
m_handle = IntPtr.Zero;
}
}
Usage:
using (LuaHandle luaHandle = new LuaHandle())
{
// do something with luaHandle.Handle
}
In this way you – as a client of LuaHandle – don’t have to deal with the extern api’s at all and no invalid IntPtr can be past as argument to lua_close()