2014-04-04 3 views
1

코드 재사용 및 유형 안전성을 위해 C++ 템플릿을 활용하고 싶지만 API 가장자리에서 템플릿 기반 구현 및 런타임 데이터 기반 외부 인터페이스를 제공합니다. 컴파일러에서 전환 작업을 수행 할 수있는 방법이 있는지 궁금합니다. (즉, 저에게이 작업 중 일부를 수행하십시오.)템플리트가 아닌 공용 API에 대한 템플릿 기반의 개인 구현 구현

이미지에 대해 몇 가지 작업을 수행하려는 고풍스러운 사례를 생각해 봅시다. 다른 색 공간으로 변환하는 것. ,

enum class ImageType : uint8_t { 
    RGB, 
    CMYK, 
    Grayscale 
}; 

그런 다음 우리는 코드를 재사용 할 템플릿 일부 개인 구현을 가지고 :

struct Image { /* Whatever */ }; 

그런 다음 우리는 우리가 지원하는 변환의 종류의 열거가 : 우리가 어떤 임의의 이미지 클래스가 말 등 :

// Internal implementation 
template <ImageType T> 
struct ImageConverter { 
public: 
    Image ConvertImage(const Image& img); 
private: 
    void some_shared_code(Image& img) { 
     // do stuff... 
    }; 
}; 

그런 다음 각 유형별로 몇 가지 메소드 인스턴스가 있습니다. (호출자에게는 모두 동일한 리턴 유형 및 매개 변수 목록을 공유한다는 점에 유의하십시오. 다음

template <> Image ImageConverter<ImageType::RGB>::ConvertImage(const Image& img) 
{ 
    Image foo = img; 
    some_shared_code(foo); 
    // do other stuff specific to this color space... 
    return foo; 
}; 

template <> Image ImageConverter<ImageType::CMYK>::ConvertImage(const Image& img) 
{ 
    Image foo = img; 
    some_shared_code(foo); 
    // do other stuff specific to this color space... 
    return foo; 
}; 

template <> Image ImageConverter<ImageType::Grayscale>::ConvertImage(const Image& img) 
{ 
    Image foo = img; 
    some_shared_code(foo); 
    // do other stuff specific to this color space... 
    return foo; 
}; 

그리고 마지막으로, 우리는이 같은 비 템플릿 API로 외부 세계에이를 팔고 다니다하려면 :

Image ConvertImage(const Image& inImage, ImageType toType) { 
    switch (toType) { 
     case ImageType::RGB: { 
      ImageConverter<ImageType::RGB> ic; 
      return ic.ConvertImage(inImage); 
     } 
     case ImageType::CMYK: { 
      ImageConverter<ImageType::CMYK> ic; 
      return ic.ConvertImage(inImage); 
     } 
     case ImageType::Grayscale: { 
      ImageConverter<ImageType::Grayscale> ic; 
      return ic.ConvertImage(inImage); 
     } 
    } 
}; 

을 그리고 그것은 나를 귀찮게 마지막 부분 - 그것은 추한 및 투박한. 가치가있는 부분은 간결성을 위해 non-type 템플릿 매개 변수를 사용하는 인위적인 예제이지만 추상 (템플릿 매개 변수가 유형 인 경우)에 문제가 있습니다.

모든 템플릿 인스턴스가 상속받는 순수 가상 "인터페이스"클래스를 선언하지만 템플릿 인스턴스가 인터페이스 클래스에서 상속되어야합니다. 제 3 자 클래스를 사용하는 경우 가끔 옵션이 아닙니다. (메모리의 레이아웃을 변경하는 것과 같은 다른 단점도 있습니다)

문장의 역할을보다 우아하게 채울 수는 있지만 그 추상적 인 공간에서 작업하는 관용구가 있습니까? 구현 (예 : 템플릿이 아닌 인터페이스 클래스에서 상속)? 이것이 공통적 인 문제 여야하고, 현재 템플릿 -fu의 범위를 벗어나는 영리한 해결책이있을 것 같습니다.

EDIT : 내가 생각하기에 더 많은 것은 아마도 템플릿 기반 메타 프로그래밍 (metaprogramming) 기반의 템플릿이라고 생각하기 시작했다.

struct ImageConverter { 
public: 
    template <typename ConvT> 
    Image ConvertImage(const Image& img, ConvT conv); 
private: 
    void some_shared_code(Image& img) { 
    // do stuff... 
    }; 
}; 

그리고 ConvertImage 전문을 :

struct RGBConv{}; 
struct CMYKConv{}; 
struct GrayscaleConv{}; 

그런 다음이 같은 ImageConverter을 선언 할 수있다) 내 마음에 오는 가능한 해결책의

답변

0

내가 뭔가를 내놓았다 :

template <typename T> 
Image ConvertImage(const Image& inImage, T conv) { 
    return ImageConverter().ConvertImage(inImage, conv); 
}; 

예, ConvertImage는 여전히 우리는이 같은 그냥 일반 함수를 호출 할 수 있습니다 ADL에 템플릿 기능을하지만, 덕분에 그런 종류의 작품이지만, 메타 데이터 프로그래밍 마법사 중 일부는 더 나은 방법이 있는지 여부에 무게를 달아야 할 것이라고 생각합니다.

우선 몇 가지 "모델"-ish 물건 :

enum class ImageType : uint8_t { 
    RGB, 
    CMYK, 
    Grayscale, 
    Invalid 
}; 

struct Image { 
    Image(ImageType type) : img_type(type) {}; 

    ImageType img_type; 
    // other stuff... 
}; 

그리고 우리의 템플릿 컨버터 클래스

template <ImageType T> 
struct ImageConverter { 
public: 
    Image ConvertImage(const Image& img); 
private: 
    void some_shared_code(Image& img) { 
     // do stuff... 
    }; 
}; 

그리고 그것의 해당 전문 버전 :

여기에 내가 무엇을 최대 온의
template <> Image ImageConverter<ImageType::RGB>::ConvertImage(const Image& img) 
{ 
    Image foo = img; 
    foo.img_type = ImageType::RGB; 
    some_shared_code(foo); 
    return foo; 
}; 

template <> Image ImageConverter<ImageType::CMYK>::ConvertImage(const Image& img) 
{ 
    Image foo = img; 
    foo.img_type = ImageType::CMYK; 
    some_shared_code(foo); 
    return foo; 
}; 

template <> Image ImageConverter<ImageType::Grayscale>::ConvertImage(const Image& img) 
{ 
    Image foo = img; 
    some_shared_code(foo); 
    foo.img_type = ImageType::Grayscale; 
    return foo; 
}; 

이제 재미있는 부분! 본질적으로 나는 가변 사이트 템플릿을 사용하여 호출 사이트에서 제공되는 옵션 목록을 반복적으로 검색했습니다.

template<ImageType T, ImageType... Args> struct _maker 
{ 
    Image operator()(ImageType desiredType, const Image& inImage) 
    { 
     if (T == desiredType) 
     { 
      auto converter = ImageConverter<T>(); 
      return converter.ConvertImage(inImage); 
     } 
     else 
     { 
      return _maker<Args...>()(desiredType, inImage); 
     } 
    }; 
}; 

이 재귀 적 템플릿 일이 끝나면 재귀의 맨 아래에서 멈출 수있는 방법이 필요했습니다. 나는 멈추는 무엇인가 가지고있는 모델에 Invalid를 덧붙여 야하는 것에 관해 오싹 오싹하지 않았다. 그러나 그것은 충분히 곧은 포워드이었다.

template<> struct _maker<ImageType::Invalid> 
{ 
    Image operator()(ImageType desiredType, const Image& inImage) 
    { 
     return Image(ImageType::Invalid); 
    }; 
}; 

는 그리고 비 템플릿 API에, 나는 다양한 옵션의 목록과 함께, 이러한 재귀 템플릿을 사용하고, 그것과 비교할 런타임 데이터 값, 입력 이미지를 전달합니다. 이런 식으로 호출

Image ConvertImage(ImageType desiredType, const Image& inImage) 
{ 
    return _maker<ImageType::RGB, ImageType::CMYK, ImageType::Grayscale, ImageType::Invalid>()(desiredType, inImage); 
}; 

: 그래서

Backtrace

이 대부분의 내 목표를 달성 :

우리가 가장 깊은 지점에서 끝낼 무엇
Image x = Image(ImageType::RGB); 
Image y = ConvertImage(ImageType::Grayscale, x); 

if (x.img_type == y.img_type) 
{ 
    cout << "not converted\n"; 
} 
else 
{ 
    cout << "converted\n"; 
} 

이 같은 역 추적입니다 switch 문을 제거합니다. 조금 깔끔한 (즉, 재귀의 끝에서 멈추기 위해 농구를 뛰어 넘을 필요가없는) 재귀 적이 아닌 경우에 좋을 것입니다. 하지만이 C++ 11 가변성 템플릿은 이미 내 행운을 쥐어주고 있습니다.

바라건대 저니 맨 템플릿 메타 프로그래머가 더 좋은 아이디어를 가지기를 바랍니다.

0

하나 열거하는 대신 형 태그를 사용하는 것입니다 각 이미지 변환기 유형 :

template <> Image ImageConverter::ConvertImage<RGBConv>(const Image& img, RGBConv conv){/*...*/} 
template <> Image ImageConverter::ConvertImage<CMYKConv>(const Image& img, CMYKConv conv){/*...*/} 
template <> Image ImageConverter::ConvertImage<GrayscaleConv>(const Image& img, GrayscaleConv conv){/*...*/} 

이제 우리는 switch 문을 제거 할 수 있습니다

ConvertImage(Image(), RGBConv()); 
ConvertImage(Image(), GrayscaleConv()); 
+0

가독성 관점에서 볼 때 더 우수하지만 API 함수를 템플릿 화해야한다는 것이 문제의 근본적인 점에서 출발합니다. 런타임 데이터와 컴파일 타임 타입을 변환하는 대신, 템플릿으로 템플릿을 푸시했습니다. – ipmcc

관련 문제