@@ -2,7 +2,7 @@ | |||
TensorFlow.NET provides .NET Standard binding for [TensorFlow](https://www.tensorflow.org/). | |||
[](https://gitter.im/sci-sharp/community) | |||
[](https://ci.appveyor.com/project/Haiping-Chen/tensorflow-net) | |||
[](https://ci.appveyor.com/project/Haiping-Chen/tensorflow-net) | |||
[](https://codecov.io/gh/SciSharp/NumSharp) | |||
[](https://www.nuget.org/packages/TensorFlow.NET) | |||
@@ -12,13 +12,13 @@ namespace Tensorflow | |||
{ | |||
var num_return_outputs = opts.NumReturnOutputs; | |||
var return_outputs = new TF_Output[num_return_outputs]; | |||
TF_Output* return_output_handle = (TF_Output*)Marshal.AllocHGlobal(Marshal.SizeOf<TF_Output>() * 2); | |||
int size = Marshal.SizeOf<TF_Output>(); | |||
TF_Output* return_output_handle = (TF_Output*)Marshal.AllocHGlobal(size * num_return_outputs); | |||
c_api.TF_GraphImportGraphDefWithReturnOutputs(_handle, graph_def, opts, return_output_handle, num_return_outputs, s); | |||
for (int i = 0; i < num_return_outputs; i++) | |||
{ | |||
var handle = return_output_handle + i * Marshal.SizeOf<TF_Output>(); | |||
var handle = return_output_handle + i * size; | |||
return_outputs[i] = new TF_Output((*handle).oper, (*handle).index); | |||
} | |||
@@ -140,15 +140,8 @@ namespace Tensorflow | |||
return ret; | |||
} | |||
public static implicit operator Operation(IntPtr handle) | |||
{ | |||
return new Operation(handle); | |||
} | |||
public static implicit operator IntPtr(Operation op) | |||
{ | |||
return op._handle; | |||
} | |||
public static implicit operator Operation(IntPtr handle) => new Operation(handle); | |||
public static implicit operator IntPtr(Operation op) => op._handle; | |||
public override bool Equals(object obj) | |||
{ | |||
@@ -14,7 +14,7 @@ namespace Tensorflow | |||
this.index = index; | |||
} | |||
public unsafe IntPtr oper; | |||
public IntPtr oper; | |||
public int index; | |||
} | |||
} |
@@ -28,11 +28,11 @@ namespace Tensorflow | |||
} | |||
_target = UTF8Encoding.UTF8.GetBytes(target); | |||
var opts = c_api.TF_NewSessionOptions(); | |||
var status = new Status(); | |||
_session = c_api.TF_NewSession(_graph, opts, status); | |||
//var opts = c_api.TF_NewSessionOptions(); | |||
//var status = new Status(); | |||
//_session = c_api.TF_NewSession(_graph, opts, status); | |||
c_api.TF_DeleteSessionOptions(opts); | |||
//c_api.TF_DeleteSessionOptions(opts); | |||
} | |||
public void Dispose() | |||
@@ -102,7 +102,7 @@ namespace Tensorflow | |||
var output_values = fetch_list.Select(x => IntPtr.Zero).ToArray(); | |||
c_api.TF_SessionRun(_session, | |||
/*c_api.TF_SessionRun(_session, | |||
run_options: IntPtr.Zero, | |||
inputs: feed_dict.Select(f => f.Key).ToArray(), | |||
input_values: new IntPtr[] { }, | |||
@@ -113,7 +113,7 @@ namespace Tensorflow | |||
target_opers: new IntPtr[] { }, | |||
ntargets: 0, | |||
run_metadata: IntPtr.Zero, | |||
status: status); | |||
status: status);*/ | |||
var result = output_values.Select(x => c_api.TF_TensorData(x)) | |||
.Select(x => (object)*(float*)x) | |||
@@ -6,5 +6,19 @@ namespace Tensorflow | |||
{ | |||
public class Session : BaseSession | |||
{ | |||
private IntPtr _handle; | |||
public Session(IntPtr handle) | |||
{ | |||
_handle = handle; | |||
} | |||
public Session(Graph graph, SessionOptions opts, Status s) | |||
{ | |||
_handle = c_api.TF_NewSession(graph, opts, s); | |||
} | |||
public static implicit operator IntPtr(Session session) => session._handle; | |||
public static implicit operator Session(IntPtr handle) => new Session(handle); | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.InteropServices; | |||
using System.Text; | |||
namespace Tensorflow | |||
{ | |||
public class SessionOptions : IDisposable | |||
{ | |||
private IntPtr _handle; | |||
public unsafe SessionOptions() | |||
{ | |||
var opts = c_api.TF_NewSessionOptions(); | |||
_handle = opts; | |||
} | |||
public unsafe SessionOptions(IntPtr handle) | |||
{ | |||
_handle = handle; | |||
} | |||
public void Dispose() | |||
{ | |||
c_api.TF_DeleteSessionOptions(_handle); | |||
} | |||
public static implicit operator IntPtr(SessionOptions opts) => opts._handle; | |||
public static implicit operator SessionOptions(IntPtr handle) => new SessionOptions(handle); | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.InteropServices; | |||
using System.Text; | |||
namespace Tensorflow | |||
{ | |||
[StructLayout(LayoutKind.Sequential)] | |||
public struct TF_SessionOptions | |||
{ | |||
public IntPtr options; | |||
} | |||
} |
@@ -10,7 +10,7 @@ namespace Tensorflow | |||
/// <summary> | |||
/// Destroy an options object. | |||
/// </summary> | |||
/// <param name="opts"></param> | |||
/// <param name="opts">TF_SessionOptions*</param> | |||
[DllImport(TensorFlowLibName)] | |||
public static unsafe extern void TF_DeleteSessionOptions(IntPtr opts); | |||
@@ -18,41 +18,63 @@ namespace Tensorflow | |||
/// Return a new execution session with the associated graph, or NULL on | |||
/// error. Does not take ownership of any input parameters. | |||
/// </summary> | |||
/// <param name="graph"></param> | |||
/// <param name="opts"></param> | |||
/// <param name="status"></param> | |||
/// <returns></returns> | |||
/// <param name="graph">TF_Graph*</param> | |||
/// <param name="opts">const TF_SessionOptions*</param> | |||
/// <param name="status">TF_Status*</param> | |||
/// <returns>TF_Session*</returns> | |||
[DllImport(TensorFlowLibName)] | |||
public static extern IntPtr TF_NewSession(IntPtr graph, IntPtr opts, IntPtr status); | |||
/// <summary> | |||
/// Return a new options object. | |||
/// </summary> | |||
/// <returns></returns> | |||
/// <returns>TF_SessionOptions*</returns> | |||
[DllImport(TensorFlowLibName)] | |||
public static extern IntPtr TF_NewSessionOptions(); | |||
public static extern unsafe IntPtr TF_NewSessionOptions(); | |||
/// <summary> | |||
/// Run the graph associated with the session starting with the supplied inputs | |||
/// (inputs[0,ninputs-1] with corresponding values in input_values[0,ninputs-1]). | |||
/// | |||
/// Any NULL and non-NULL value combinations for (`run_options`, | |||
/// `run_metadata`) are valid. | |||
/// | |||
/// - `run_options` may be NULL, in which case it will be ignored; or | |||
/// non-NULL, in which case it must point to a `TF_Buffer` containing the | |||
/// serialized representation of a `RunOptions` protocol buffer. | |||
/// - `run_metadata` may be NULL, in which case it will be ignored; or | |||
/// non-NULL, in which case it must point to an empty, freshly allocated | |||
/// `TF_Buffer` that may be updated to contain the serialized representation | |||
/// of a `RunMetadata` protocol buffer. | |||
/// | |||
/// The caller retains ownership of `input_values` (which can be deleted using | |||
/// TF_DeleteTensor). The caller also retains ownership of `run_options` and/or | |||
/// `run_metadata` (when not NULL) and should manually call TF_DeleteBuffer on | |||
/// them. | |||
/// | |||
/// On success, the tensors corresponding to outputs[0,noutputs-1] are placed in | |||
/// output_values[]. Ownership of the elements of output_values[] is transferred | |||
/// to the caller, which must eventually call TF_DeleteTensor on them. | |||
/// | |||
/// On failure, output_values[] contains NULLs. | |||
/// </summary> | |||
/// <param name="session"></param> | |||
/// <param name="run_options"></param> | |||
/// <param name="inputs">TF_Output</param> | |||
/// <param name="input_values">TF_Tensor</param> | |||
/// <param name="ninputs"></param> | |||
/// <param name="outputs"></param> | |||
/// <param name="output_values"></param> | |||
/// <param name="noutputs"></param> | |||
/// <param name="target_opers"></param> | |||
/// <param name="ntargets"></param> | |||
/// <param name="run_metadata"></param> | |||
/// <param name="status"></param> | |||
/// <param name="session">TF_Session*</param> | |||
/// <param name="run_options">const TF_Buffer*</param> | |||
/// <param name="inputs">const TF_Output*</param> | |||
/// <param name="input_values">TF_Tensor* const*</param> | |||
/// <param name="ninputs">int</param> | |||
/// <param name="outputs">const TF_Output*</param> | |||
/// <param name="output_values">TF_Tensor**</param> | |||
/// <param name="noutputs">int</param> | |||
/// <param name="target_opers">const TF_Operation* const*</param> | |||
/// <param name="ntargets">int</param> | |||
/// <param name="run_metadata">TF_Buffer*</param> | |||
/// <param name="status">TF_Status*</param> | |||
[DllImport(TensorFlowLibName)] | |||
public static extern unsafe void TF_SessionRun(IntPtr session, IntPtr run_options, | |||
TF_Output[] inputs, IntPtr[] input_values, int ninputs, | |||
TF_Output[] outputs, IntPtr[] output_values, int noutputs, | |||
IntPtr[] target_opers, int ntargets, | |||
public static extern unsafe void TF_SessionRun(IntPtr session, TF_Buffer* run_options, | |||
IntPtr inputs, IntPtr input_values, int ninputs, | |||
IntPtr outputs, ref IntPtr output_values, int noutputs, | |||
IntPtr target_opers, int ntargets, | |||
IntPtr run_metadata, | |||
IntPtr status); | |||
} | |||
@@ -0,0 +1,15 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.InteropServices; | |||
using System.Text; | |||
namespace Tensorflow | |||
{ | |||
[StructLayout(LayoutKind.Sequential)] | |||
public struct TF_Tensor | |||
{ | |||
public TF_DataType dtype; | |||
public IntPtr shape; | |||
public IntPtr buffer; | |||
} | |||
} |
@@ -22,12 +22,22 @@ namespace Tensorflow | |||
public object value; | |||
public int value_index { get; } | |||
public TF_DataType dtype { get; } | |||
public ulong bytesize { get; } | |||
public ulong dataTypeSize { get;} | |||
public ulong size => bytesize / dataTypeSize; | |||
public IntPtr buffer { get; } | |||
public long[] shape { get; } | |||
public TF_DataType dtype => _handle == IntPtr.Zero ? TF_DataType.DtInvalid : c_api.TF_TensorType(_handle); | |||
public ulong bytesize => _handle == IntPtr.Zero ? 0 : c_api.TF_TensorByteSize(_handle); | |||
public ulong dataTypeSize => _handle == IntPtr.Zero ? 0 : c_api.TF_DataTypeSize(dtype); | |||
public ulong size => _handle == IntPtr.Zero ? 0 : bytesize / dataTypeSize; | |||
public IntPtr buffer => _handle == IntPtr.Zero ? IntPtr.Zero : c_api.TF_TensorData(_handle); | |||
public long[] shape | |||
{ | |||
get | |||
{ | |||
var dims = new long[rank]; | |||
for (int i = 0; i < rank; i++) | |||
shape[i] = c_api.TF_Dim(_handle, i); | |||
return dims; | |||
} | |||
} | |||
/// <summary> | |||
/// number of dimensions | |||
@@ -37,7 +47,7 @@ namespace Tensorflow | |||
/// 3 3-Tensor (cube of numbers) | |||
/// n n-Tensor (you get the idea) | |||
/// </summary> | |||
public int rank; | |||
public int rank => _handle == IntPtr.Zero ? 0 : c_api.TF_NumDims(_handle); | |||
public int NDims => rank; | |||
/// <summary> | |||
@@ -48,29 +58,12 @@ namespace Tensorflow | |||
public Tensor(IntPtr handle) | |||
{ | |||
_handle = handle; | |||
dtype = c_api.TF_TensorType(handle); | |||
rank = c_api.TF_NumDims(handle); | |||
bytesize = c_api.TF_TensorByteSize(handle); | |||
buffer = c_api.TF_TensorData(handle); | |||
dataTypeSize = c_api.TF_DataTypeSize(dtype); | |||
shape = new long[rank]; | |||
for (int i = 0; i < rank; i++) | |||
shape[i] = c_api.TF_Dim(handle, i); | |||
} | |||
public Tensor(NDArray nd) | |||
{ | |||
_handle = Allocate(nd); | |||
dtype = c_api.TF_TensorType(_handle); | |||
rank = c_api.TF_NumDims(_handle); | |||
bytesize = c_api.TF_TensorByteSize(_handle); | |||
buffer = c_api.TF_TensorData(_handle); | |||
dataTypeSize = c_api.TF_DataTypeSize(dtype); | |||
shape = new long[rank]; | |||
for (int i = 0; i < rank; i++) | |||
shape[i] = c_api.TF_Dim(_handle, i); | |||
value = nd.Data(); | |||
} | |||
private IntPtr Allocate(NDArray nd) | |||
@@ -113,7 +106,6 @@ namespace Tensorflow | |||
{ | |||
this.op = op; | |||
this.value_index = value_index; | |||
this.dtype = dtype; | |||
} | |||
public TF_Output _as_tf_output() | |||
@@ -146,6 +138,12 @@ namespace Tensorflow | |||
return data; | |||
} | |||
public Tensor MaybeMove() | |||
{ | |||
var tensor = c_api.TF_TensorMaybeMove(_handle); | |||
return tensor; | |||
} | |||
public TF_DataType ToTFDataType(Type type) | |||
{ | |||
switch (type.Name) | |||
@@ -73,6 +73,15 @@ namespace Tensorflow | |||
[DllImport(TensorFlowLibName)] | |||
public static extern IntPtr TF_TensorData(IntPtr tensor); | |||
/// <summary> | |||
/// Deletes `tensor` and returns a new TF_Tensor with the same content if | |||
/// possible. Returns nullptr and leaves `tensor` untouched if not. | |||
/// </summary> | |||
/// <param name="tensor"></param> | |||
/// <returns></returns> | |||
[DllImport(TensorFlowLibName)] | |||
public static extern IntPtr TF_TensorMaybeMove(IntPtr tensor); | |||
/// <summary> | |||
/// Return the type of a tensor element. | |||
/// </summary> | |||
@@ -51,7 +51,7 @@ namespace Tensorflow | |||
public static Session Session() | |||
{ | |||
return new Session(); | |||
return (Session)new BaseSession(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,100 @@ | |||
using NumSharp.Core; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.InteropServices; | |||
using System.Text; | |||
using Tensorflow; | |||
namespace TensorFlowNET.UnitTest | |||
{ | |||
/// <summary> | |||
/// tensorflow\c\c_test_util.cc | |||
/// </summary> | |||
public class CSession | |||
{ | |||
private IntPtr session_; | |||
private List<IntPtr> inputs_ = new List<IntPtr>(); | |||
private List<IntPtr> input_values_ = new List<IntPtr>(); | |||
private List<IntPtr> outputs_ = new List<IntPtr>(); | |||
private List<IntPtr> output_values_ = new List<IntPtr>(); | |||
private List<IntPtr> targets_ = new List<IntPtr>(); | |||
public CSession(Graph graph, Status s, bool user_XLA = false) | |||
{ | |||
var opts = new SessionOptions(); | |||
session_ = new Session(graph, opts, s); | |||
} | |||
public void SetInputs(Dictionary<IntPtr, IntPtr> inputs) | |||
{ | |||
DeleteInputValues(); | |||
inputs_.Clear(); | |||
foreach (var input in inputs) | |||
{ | |||
var handle = Marshal.AllocHGlobal(Marshal.SizeOf<TF_Output>()); | |||
Marshal.StructureToPtr(new TF_Output(input.Key, 0), handle, false); | |||
inputs_.Add(handle); | |||
input_values_.Add(input.Value); | |||
} | |||
} | |||
private void DeleteInputValues() | |||
{ | |||
for (var i = 0; i < input_values_.Count; ++i) | |||
{ | |||
//input_values_[i].Dispose(); | |||
} | |||
input_values_.Clear(); | |||
} | |||
public void SetOutputs(List<IntPtr> outputs) | |||
{ | |||
ResetOutputValues(); | |||
outputs_.Clear(); | |||
foreach (var output in outputs) | |||
{ | |||
var handle = Marshal.AllocHGlobal(Marshal.SizeOf<TF_Output>()); | |||
Marshal.StructureToPtr(new TF_Output(output, 0), handle, true); | |||
outputs_.Add(handle); | |||
handle = Marshal.AllocHGlobal(Marshal.SizeOf<IntPtr>()); | |||
output_values_.Add(IntPtr.Zero); | |||
} | |||
} | |||
private void ResetOutputValues() | |||
{ | |||
for (var i = 0; i < output_values_.Count; ++i) | |||
{ | |||
//if (output_values_[i] != IntPtr.Zero) | |||
//output_values_[i].Dispose(); | |||
} | |||
output_values_.Clear(); | |||
} | |||
public unsafe void Run(Status s) | |||
{ | |||
IntPtr inputs_ptr = inputs_.Count == 0 ? IntPtr.Zero : inputs_[0]; | |||
IntPtr input_values_ptr = inputs_.Count == 0 ? IntPtr.Zero : input_values_[0]; | |||
IntPtr outputs_ptr = outputs_.Count == 0 ? IntPtr.Zero : outputs_[0]; | |||
IntPtr output_values_ptr = output_values_.Count == 0 ? IntPtr.Zero : output_values_[0]; | |||
IntPtr targets_ptr = IntPtr.Zero; | |||
c_api.TF_SessionRun(session_, null, inputs_ptr, input_values_ptr, inputs_.Count, | |||
outputs_ptr, ref output_values_ptr, outputs_.Count, | |||
targets_ptr, targets_.Count, | |||
IntPtr.Zero, s); | |||
s.Check(); | |||
output_values_[0] = output_values_ptr; | |||
} | |||
public IntPtr output_tensor(int i) | |||
{ | |||
return output_values_[i]; | |||
} | |||
} | |||
} |
@@ -16,7 +16,7 @@ namespace TensorFlowNET.UnitTest | |||
/// `TEST(CAPI, Graph)` | |||
/// </summary> | |||
[TestMethod] | |||
public void c_api_Graph() | |||
public void Graph() | |||
{ | |||
var s = new Status(); | |||
var graph = new Graph(); | |||
@@ -205,7 +205,7 @@ namespace TensorFlowNET.UnitTest | |||
/// `TEST(CAPI, ImportGraphDef)` | |||
/// </summary> | |||
[TestMethod] | |||
public void c_api_ImportGraphDef() | |||
public void ImportGraphDef() | |||
{ | |||
var s = new Status(); | |||
var graph = new Graph(); | |||
@@ -362,7 +362,7 @@ namespace TensorFlowNET.UnitTest | |||
/// `TEST(CAPI, ImportGraphDef_WithReturnOutputs)` | |||
/// </summary> | |||
[TestMethod] | |||
public void c_api_ImportGraphDef_WithReturnOutputs() | |||
public void ImportGraphDef_WithReturnOutputs() | |||
{ | |||
var s = new Status(); | |||
var graph = new Graph(); | |||
@@ -407,5 +407,16 @@ namespace TensorFlowNET.UnitTest | |||
graph.Dispose(); | |||
s.Dispose(); | |||
} | |||
/// <summary> | |||
/// `TEST(CAPI, ImportGraphDef_MissingUnusedInputMappings)` | |||
/// </summary> | |||
[TestMethod] | |||
public void ImportGraphDef_MissingUnusedInputMappings() | |||
{ | |||
} | |||
} | |||
} |
@@ -15,7 +15,7 @@ namespace TensorFlowNET.UnitTest | |||
/// `TEST(CAPI, GetAllOpList)` | |||
/// </summary> | |||
[TestMethod] | |||
public void c_api_GetAllOpList() | |||
public void GetAllOpList() | |||
{ | |||
var handle = c_api.TF_GetAllOpList(); | |||
var buffer = new Buffer(handle); | |||
@@ -0,0 +1,51 @@ | |||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using Tensorflow; | |||
namespace TensorFlowNET.UnitTest | |||
{ | |||
[TestClass] | |||
public class SessionTest : CApiTest | |||
{ | |||
/// <summary> | |||
/// tensorflow\c\c_api_test.cc | |||
/// `TEST(CAPI, Session)` | |||
/// </summary> | |||
[TestMethod] | |||
public void Session() | |||
{ | |||
var s = new Status(); | |||
var graph = new Graph(); | |||
// Make a placeholder operation. | |||
var feed = c_test_util.ScalarConst(3, graph, s, "scalar1"); //c_test_util.Placeholder(graph, s); | |||
// Make a constant operation with the scalar "2". | |||
var two = c_test_util.ScalarConst(2, graph, s, "scalar2"); | |||
// Add operation. | |||
var add = c_test_util.Add(feed, two, graph, s); | |||
var csession = new CSession(graph, s); | |||
ASSERT_EQ(TF_Code.TF_OK, s.Code); | |||
// Run the graph. | |||
var inputs = new Dictionary<IntPtr, IntPtr>(); | |||
inputs.Add(feed, c_test_util.Int32Tensor(3)); | |||
//csession.SetInputs(inputs); | |||
var outputs = new List<IntPtr> { add }; | |||
csession.SetOutputs(outputs); | |||
csession.Run(s); | |||
Tensor outTensor = csession.output_tensor(0); | |||
EXPECT_EQ(TF_DataType.TF_INT32, outTensor.dtype); | |||
EXPECT_EQ(0, outTensor.NDims); | |||
ASSERT_EQ((ulong)sizeof(uint), outTensor.bytesize); | |||
var output_contents = outTensor.Data<int>(); | |||
EXPECT_EQ(3 + 2, output_contents[0]); | |||
} | |||
} | |||
} |
@@ -17,7 +17,7 @@ namespace TensorFlowNET.UnitTest | |||
/// `TEST(CAPI, AllocateTensor)` | |||
/// </summary> | |||
[TestMethod] | |||
public void c_api_AllocateTensor() | |||
public void AllocateTensor() | |||
{ | |||
ulong num_bytes = 6 * sizeof(float); | |||
long[] dims = { 2, 3 }; | |||
@@ -29,12 +29,26 @@ namespace TensorFlowNET.UnitTest | |||
t.Dispose(); | |||
} | |||
/// <summary> | |||
/// Port from c_api_test.cc | |||
/// `TEST(CAPI, MaybeMove)` | |||
/// </summary> | |||
[TestMethod] | |||
public void MaybeMove() | |||
{ | |||
NDArray nd = np.array(2, 3); | |||
Tensor t = new Tensor(nd); | |||
Tensor o = t.MaybeMove(); | |||
ASSERT_TRUE(o == IntPtr.Zero); // It is unsafe to move memory TF might not own. | |||
t.Dispose(); | |||
} | |||
/// <summary> | |||
/// Port from c_api_test.cc | |||
/// `TEST(CAPI, Tensor)` | |||
/// </summary> | |||
[TestMethod] | |||
public void c_api_Tensor() | |||
public void Tensor() | |||
{ | |||
var nd = np.array(1f, 2f, 3f, 4f, 5f, 6f).reshape(2, 3); | |||
@@ -54,7 +68,7 @@ namespace TensorFlowNET.UnitTest | |||
/// `TEST(CAPI, SetShape)` | |||
/// </summary> | |||
[TestMethod] | |||
public void c_api_SetShape() | |||
public void SetShape() | |||
{ | |||
var s = new Status(); | |||
var graph = new Graph(); | |||
@@ -236,5 +236,19 @@ namespace TensorFlowNET.UnitTest | |||
{ | |||
return Const(new Tensor(v), graph, s, name); | |||
} | |||
public static unsafe IntPtr Int32Tensor(int v) | |||
{ | |||
bool deallocator_called = false; | |||
const int num_bytes = sizeof(int); | |||
var dotHandle = Marshal.AllocHGlobal(num_bytes * 1); | |||
*(int*)dotHandle = v; | |||
return c_api.TF_NewTensor(TF_DataType.TF_INT32, new long[0], 0, dotHandle, num_bytes, | |||
(IntPtr values, IntPtr len, ref bool closure) => | |||
{ | |||
// Free the original buffer and set flag | |||
Marshal.FreeHGlobal(dotHandle); | |||
}, ref deallocator_called); | |||
} | |||
} | |||
} |