2010-04-11 1 views
3

저는 현재 매우 큰 이미지 볼륨을 가지고있는 프로젝트에 참여하고 있습니다. 이 볼륨은 매우 빠르게 처리해야합니다 (더하기, 빼기, 임계 값 등). 또한 볼륨의 대부분이 너무 커서 이벤트가 시스템의 메모리에 맞지 않습니다. 그런 이유로 나는 볼륨과 이미지 데이터를 호스트하는 추상 볼륨 클래스 (VoxelVolume)를 생성하고 볼륨에 대한 정규 수학 연산을 수행 할 수 있도록 연산자를 오버로드합니다. 따라서 두 개의 추가 질문이 stackoverflow에 추가 될 질문이 두 개 더 열렸습니다.C# 일반 배열 및 수학 연산

여기 내 첫 번째 질문입니다. 내 볼륨은 플로트 배열 데이터 만 포함 할 수있는 방식으로 구현되지만 대부분의 포함 데이터는 UInt16 이미지 소스에서 가져온 것입니다. 볼륨에 대한 작업 만 float 배열 이미지를 만들 수 있습니다.

나는 클래스가 다음처럼 보였다 같은 볼륨 구현 시작했을 때 :

public abstract class VoxelVolume<T> 
{ 
... 
} 

을하지만 그때 나는 연산자를 오버로드 또는 반환 값이 더 복잡 얻을 것이라는 점을 깨달았다. 예제는 다음과 같습니다

... 
public static VoxelVolume<T> operator+(VoxelVolume<T> A, VoxelVolume<T> B) 
{ 
... 
}  

이의는, 그럼에도 불구하고 나는 이미지를 포함하는 배열의 다른 유형을 가지고 내가 위에서 설명한 문제를 극복 할 수 가정 해 봅시다 :

public abstract class VoxelVolume<T> 
{ 
... 
    public static VoxelVolume<T> Import<T>(param string[] files) 
    { 
    } 
} 

도 추가 두 개의 오버로드 운영자가 더 복잡 것 데이터. 플로트 할 볼륨에 타입을 고정 했으므로 두 개의 이미지 볼륨 어레이의 내용을 추가 할 때 안전하지 않은 조작을 할 수 있습니다. 여기서 몇 가지 스레드를 읽고 웹을 둘러 보았지만 다른 유형의 배열 두 개를 빠르게 추가하려는 경우 수행 할 작업에 대한 실제 설명이 없습니다. 불행히도 C#에서는 기본 데이터 유형의 크기를 계산할 수 없기 때문에 제네릭에 대한 모든 수학 연산을 수행 할 수 없습니다. 물론 C++/CLR을 사용하여이 문제를 해결할 수도 있지만 현재까지 내가 한 모든 작업은 일을하지 않고도 32 비트 및 64 비트에서 실행됩니다. C++/CLR로 전환하는 것이 (특정 플랫폼 (32 비트)에 바인딩되어 있다는 잘못된 생각이 들었을 때 나에게 기분이 좋았다.) 애플리케이션을 다른 플랫폼 (64 비트)에서 실행할 때 어셈블리를 두 개 컴파일해야했다. 사실입니까?

빠른 시일 내에 두 가지 유형의 배열 두 개를 어떻게 추가 할 수 있습니까? C# 개발자가이 문제에 대해 생각해 본 것이 사실입니까? 다른 언어 (C# -> C++)로 전환하는 것이 옵션이 아닌 것 같습니다.

나는 그것이 작동한다면 그것은 좋은 것입니다하지만 단순히이 작업

float []A = new float[]{1,2,3}; 
byte []B = new byte[]{1,2,3}; 

float []C = A+B; 

을 수행하는 것이 가능하고 불필요한 아니라는 것을 알고 있습니다. 나는 다음과 같은 한려고 내 솔루션 : 위의 코드는 매우 정확하지 않은 경우

public static class ArrayExt 
{ 
    public static unsafe TResult[] Add<T1, T2, TResult>(T1 []A, T2 []B) 
    { 
     // Assume the length of both arrays is equal 
     TResult[] result = new TResult[A.Length]; 

     GCHandle h1 = GCHandle.Alloc (A, Pinned); 
     GCHandle h2 = GCHandle.Alloc (B, Pinned); 
     GCHandle hR = GCHandle.Alloc (C, Pinned); 

     void *ptrA = h1.ToPointer(); 
     void *ptrB = h2.ToPointer(); 
     void *ptrR = hR.ToPointer(); 

     for (int i=0; i<A.Length; i++) 
     { 
      *((TResult *)ptrR) = (TResult *)((T1)*ptrA + (T2)*ptrB)); 
     } 

     h1.Free(); 
     h2.Free(); 
     hR.Free(); 

     return result; 
    } 
} 

는 서하십시오, 나는 C#을 편집기를 사용하지 않고 그것을 썼다. 그런 해결책이 생각보다 위에 보이나요? 제가 실수를했거나 불완전하게 몇 가지를 설명했는지 물어보십시오. 당신의 도움이
마틴

+0

이 질문을 삭제하고 비 커뮤니티 위키로 다시 요청해야합니다. (먼저, '편집'을 클릭하고 소스를 복사하십시오.) – SLaks

+1

CW가 아니어도 다시 요청할 필요는 없습니다. –

답변

1

에 대한

덕분에 이는 "우리가 INumerical 인터페이스를하지 않는 이유"의 (복잡한) 버전을 보인다.

마지막 질문에 대한 짧은 대답은 : 아니오, 안전하지 않은 포인터로가는 것은 해결책이 아니며 컴파일러는 여전히 에 +을 알아낼 수 없습니다. 당신이 floatUInt32 같은 몇 가지 유형이있는 경우

+0

안녕하세요 헨크. 답변 주셔서 감사합니다. 그것이 내가 알아 낸 것입니다. 나는 이것을위한 해결책이있을 수 있기를 바랬다. – msedi

1

VoxelVolume<float>VoxelVolume<UInt32>에서 예를 들어, 필요한 모든 변환 기능을 제공하고 VoxelVolume<float>에 수학을. 그것은 가장 실제적인 경우에 충분히 빠를 것입니다. VoxelVolume<T1>에서 VoxelVolume<T2>까지 일반 변환 기능을 제공 할 수도 있습니다 (T1이 T2로 변환 가능한 경우). 다른 한편으로는, 당신은 정말 당신은 연산자를 쓰는 것을 방해, 각 배열 요소에 대한 T2-T1에서 타입 변환과 함께

public static VoxelVolume<T2> operator+(VoxelVolume<T1> A,VoxelVolume<T2> B) 

필요하면?

+0

안녕하세요 닥 브라운입니다. 귀하의 솔루션이 올바른 방향으로 나를 끌어들일 것 같습니다. 나는 그것에 대해 생각합니다. 일반적으로 C#에서 사용할 수있는 모든 필수 데이터 유형을 사용할 수있는 곳을 만들고 싶다면 가능한 모든 데이터 유형에 대한 오버로드를 작성해야하지만 합리적인 것처럼 보입니다.하지만 몇 가지 데이터 유형 많은 도움이 될 것입니다. – msedi

1

가져 오기는 제네릭 클래스의 구성원이기 때문에 일반적으로 자체가 될 필요는 없습니다. 그렇다면 클래스에 대한 일반 매개 변수와 함수에 대한 일반 매개 변수 모두에 동일한 이름 인 T을 사용하면 안됩니다.

은 당신이 아마 찾고있는 것은 마크 Gravell의 Generic Operators

다음 typename T에 대한 모든 가능한 값은 컴파일에서 제어하기 때문에 대신 제네릭의 템플릿을 사용하는 경우 C++/CLI에 대한 질문에 관해서는, 그래이 도움이 될 수 시간과 컴파일러는 각각에 대한 연산자를 찾습니다. 또한 /clr:pure 또는 /clr:safe을 사용할 수 있습니다.이 경우 코드는 C#과 마찬가지로 AnyCPU에서 실행할 수있는 MSIL이됩니다.

+0

안녕하세요, 귀하의 의견에 감사드립니다. 너가 확실히 맞아. 나는 운전자에게 동일한 T를 사용하려고하지 않았다. 이것은 단지 예일 뿐이며 내가하고 싶은 것의 복잡성을 보여 주어야합니다. – msedi

+0

Marc의 멋진 기사이지만, 컴파일 타임 형 안전성을 얻지 못하면 "동적"을 사용하여 문제를 쉽게 해결할 수 있다고 생각됩니다. – CompuChip

+0

@CompuChip :이 질문 당시 '동적'키워드를 사용할 수 없었습니다. –

0

인정 하듯이, 나는 (그것이 꽤 너무 오래입니다) 전체 질문을 읽을 수 있지만하지 않았다 :

  1. VoxelVolume<T> where T : ISummand ... T a; a.Add(b)
  2. static float Sum (this VoxelVolume<float> self, VoxelVolume<float> other) {...}
  3. 어떤 의미 의미에서 바이트로 플로트를 추가하려면 바이트를 float로 변환해야합니다. 그래서 배열을 float의 배열로 변환 한 다음 추가하면 일부 메모리 만 손실됩니다.
+0

안녕 이마, 네 말이 맞아, 길었어. 가능한 한 자세하게 설명하고 싶었습니다. 당신은 당연히 가치를 던질 수 있습니다. 그러나 저에게는 앞으로의 프로젝트가 그러한 작업을 수행하는보다 일반적인 방법을 갖도록 도와 줄 것 같았습니다. – msedi