1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

unsafeコンテキスト以外でIntPtrからSpan<T>またはReadOnlySpan<T>に無理矢理変換する方法

Last updated at Posted at 2019-04-17

How to force conversion from IntPtr to Span<T> or ReadOnlySpan<T> without unsafe context

以前の記事IntPtrからSpan/ReadOnlySpan に変換できないこと書いたが、無理矢理なんとかしてみる。

力技といえば動的コード生成。

ソースコード

多分こんなので動くはず。

using System;
using static System.Linq.Expressions.Expression;

namespace IntPtrToSpan
{
    public static class IntPtrToSpanExtention
    {
        public static Span<T> AsSpan<T>(this IntPtr pointer, int length) =>
            //return new Span<T>(pointer.ToPointer(), length);
            Impl<T>.Delegate(pointer, length);
        public static ReadOnlySpan<T> AsReadOnlySpan<T>(this IntPtr pointer, int length) =>
            //return new ReadOnlySpan<T>(pointer.ToPointer(), length);
            Impl<T>.ReadOnlyDelegate(pointer, length);

        private delegate Span<T> AsSpanDelegate<T>(IntPtr pointer, int length);
        private delegate ReadOnlySpan<T> AsReadOnlySpanDelegate<T>(IntPtr pointer, int length);
        static class Impl<T>
        {
            private static Func Generate<Func>(bool readOnly)
            {
                var pointer = Parameter(typeof(IntPtr), "pointer");
                var length = Parameter(typeof(int), "length");

                var constructor = (readOnly ? typeof(ReadOnlySpan<T>) : typeof(Span<T>)).GetConstructor(new[] { typeof(void*), typeof(int) });
                var lambda = Lambda<Func>(
                    New(constructor,
                        Call(
                            pointer,
                            typeof(IntPtr).GetMethod(nameof(IntPtr.ToPointer), Type.EmptyTypes)),
                        length),
                    pointer,
                    length
                    );
                return lambda.Compile();
            }
            public static AsSpanDelegate<T> Delegate = Generate<AsSpanDelegate<T>>(false);
            public static AsReadOnlySpanDelegate<T> ReadOnlyDelegate = Generate<AsReadOnlySpanDelegate<T>>(true);
        }
    }
}

個人的なハマりポイント

  • Func の型引数で ref struct を指定できないので、デリゲートを定義した。
  • Lambdaのパラメータと式中のExpressionで別々のインスタンスを使用していたら、InvalidOperationException: variable 'someVariable' of type 'SomeType' referenced from scope '', but it is not definedって例外が出て「定義されているやろ」って悩んだ。
  • コンパイラの生成した式ツリーを参考にしようとExpression<Func<IntPtr,int, Span<T>>> a = (pointer, length) => new Span<T>(pointer.ToPointer(), length);をコンパイルしようとしたら、ref structを式ツリーに含められないようでコンパイルエラーになった。
  • typeof(void*) ってunsafeコンテキスト以外だとどうやっってとってきたらいいんだ?」って思ったら、unsafeコンテキストじゃなくても大丈夫だった。
1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?