2017년 2월 16일 목요일

C#에서의 RAII(Resource Acquisition Is Initialization) idiom 구현

C++에서는 file이나 DB의 resoruce leak 처리를 위해서
RAII idiom을 자주 사용하곤 했었는데,

http://en.cppreference.com/w/cpp/language/raii
https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

#include <mutex>
#include <iostream>
#include <string> 
#include <fstream>
#include <stdexcept>

void write_to_file (const std::string & message) {
    // mutex to protect file access (shared across threads)
    static std::mutex mutex;

    // lock mutex before accessing file
    std::lock_guard<std::mutex> lock(mutex);

    // try to open file
    std::ofstream file("example.txt");
    if (!file.is_open())
        throw std::runtime_error("unable to open file");
    
    // write message to file
    file << message << std::endl;
    
    // file will be closed 1st when leaving scope (regardless of exception)
    // mutex will be unlocked 2nd (from lock destructor) when leaving
    // scope (regardless of exception)
}

C#에서는 destructor 사용에 신중해져야 해서
찾아보니 다음과 같은 글들이 있어 링크함.


요약하면 
- IDisposable 인터페이스를 구현하고
- 객체를 항상 삭제하는 using 문을 이용하여 RAII 객체를 사용하라는 것


MSDN의 IDisposable 설명에서도 언급하고 있다.

IDisposable을 구현 하는 개체를 사용 하 여

앱이 IDisposable 인터페이스를 구현 하는 개체를 사용 하는 경우 개체의 IDisposable.Dispose 구현을 사용 하는 경우 해당 개체의 구현을 호출 해야 합니다. 프로그래밍 언어에 따라 두 가지 방법 중 하나에서이 수행할 수 있습니다.

  • 및 Visual Basic에서 C# using 문과 같은 언어 구문을 사용 합니다.

  • try/finally 블록에 IDisposable.Dispose 구현에 대 한 호출을 래핑합니다.




RAII (Resource Acquisition Is Initialization) C# Helper Classes 예제
https://www.codeproject.com/Articles/122129/RAII-Resource-Acquisition-Is-Initialization-C-Help

using (var objGuard = new RAIIGuard&lt;int>(
                  () =>objStack.Pop(),
                  (e)=>objStack.Push(e)))
{
    ...
    if (objGuard.Item != 0)
    {
        return;
    }   
    ...
    if (...)
    {
         throw new ...;
    }
    ...
}



// used for symmetric actions like login, logout
public sealed class RAIIGuard: IDisposable
{
    private Action Cleanup { get; set; }
    public RAIIGuard(Action init, Action cleanup)
    {
        Cleanup = cleanup;
        if (init != null) init();
    }
    void IDisposable.Dispose() { if (Cleanup != null) Cleanup(); }
]
// used for symmetric actions that must pass
// over an object from init to cleanup and that
// need to provide the item to the "using" body
public sealed class RAIIGuard&lt;T>: IDisposable
{
    private Action&lt;T> Cleanup { get; set; }
    public T Item { get; private set; }
    public RAIIGuard(Func&lt;T> init, Action&lt;T> cleanup)
    {
        Cleanup = cleanup;
        Item = (init != null) ? init() : default(T);
    }
    void IDisposable.Dispose() { if (Cleanup != null) Cleanup(Item); }
]



Resource Acquisition is Initialization in C#
http://geekswithblogs.net/codeWithoutFear/archive/2012/06/28/raii-in-csharp.aspx

Here is a class and sample that combines a few features of C# to provide an RAII-like solution:

using System;

namespace RAII
{
    public class DisposableDelegate : IDisposable
    {
        private Action dispose;

        public DisposableDelegate(Action dispose)
        {
            if (dispose == null)
            {
                throw new ArgumentNullException("dispose");
            }

            this.dispose = dispose;
        }

        public void Dispose()
        {
            if (this.dispose != null)
            {
                Action d = this.dispose;
                this.dispose = null;
                d();
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.Out.WriteLine("Some resource allocated here.");

            using (new DisposableDelegate(() => Console.Out.WriteLine("Resource deallocated here.")))
            {
                Console.Out.WriteLine("Resource used here.");

                throw new InvalidOperationException("Test for resource leaks.");
            }
        }
    }
}

The output of this program is:

Some resource allocated here.
Resource used here.

Unhandled Exception: System.InvalidOperationException: Test for resource leaks.
   at RAII.Program.Main(String[] args) in c:\Dev\RAII\RAII\Program.cs:line 40
Resource deallocated here.







2017년 2월 15일 수요일

64bit architecture에서의 char* pointer byte size

C#에서 C 기반의 library에서 char**을 가져오려고
계속 시도하는데 죽는 현상이 발생해서 삽질을 하고 있었음.

Interop with Native Libraries: http://www.mono-project.com/docs/advanced/pinvoke/

: https://bytes.com/topic/c-sharp/answers/554501-c-c-interop-returning-char

계속 전달되는 char**의 IntPtr에서 char*를 읽기 위해 4bytes씩 읽어오도록 하고 있었는데..

정말 바보 같았다. malloc, memset에서도 sizeof를 그렇게 줄창 쓰면서도
왜!!! 바보같이 4bytes 읽어 왔었는지 급 한심스러워짐..

일단 MS에서도 아래와 같이 가이드를 주고 있음.

하지만 Marshal.SizeOf(typeof(IntPtr)) 보다는
Marshal.SizeOf<IntPtr>()이 warning이 뜨지 않음.


https://msdn.microsoft.com/ko-kr/library/0t7xwf59(v=vs.110).aspx
다음 예제에서는 읽기 / 쓰기를 사용 하 여 관리 되지 않는 배열 하는 방법의 ReadIntPtr  WriteIntPtr 메서드.
static void ReadWriteIntPtr()
{
    // Allocate unmanaged memory. 
    int elementSize = Marshal.SizeOf(typeof(IntPtr));
    IntPtr unmanagedArray = Marshal.AllocHGlobal(10 * elementSize);

    // Set the 10 elements of the C-style unmanagedArray
    for (int i = 0; i < 10; i++)
    {
        Marshal.WriteIntPtr(unmanagedArray, i * elementSize, ((IntPtr)(i + 1)));
    }
    Console.WriteLine("Unmanaged memory written.");

    Console.WriteLine("Reading unmanaged memory:");
    // Print the 10 elements of the C-style unmanagedArray
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(Marshal.ReadIntPtr(unmanagedArray, i * elementSize));
    }

    Marshal.FreeHGlobal(unmanagedArray);

    Console.WriteLine("Done. Press Enter to continue.");
    Console.ReadLine();
}