4
\$\begingroup\$

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?

\$\endgroup\$
5
\$\begingroup\$

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()

\$\endgroup\$
  • \$\begingroup\$ Hadn't considered a wrapper like this. Thanks! \$\endgroup\$ – Riley G Mar 5 '18 at 19:08
  • \$\begingroup\$ Typo at public selaed? \$\endgroup\$ – hjpotter92 Mar 6 '18 at 17:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.