vector와 가변템플릿에 대해서 질문하나 하겠습니다.

jehnpark의 이미지

template < class... T >
int f1(int(*f2)(T...),vector <void* > v);
vector v의 각 원소들을 T 포인터로 캐스팅한 후에 f2에 넣은 것을 반환하는 함수를 만들고 싶습니다.
v의 원소들을 T포인터로 캐스팅해 tuple로 만드는 것만 할 줄 알면 쉽게 풀릴거 같은데 많이 어렵습니다.

익명 사용자의 이미지

C++03 시절에도 템플릿은 제 C++ 프로그래밍 지식/경험의 끝자락에 가까스로 걸치던 물건이었습니다만, C++11 이후로는 아예 넘사벽이로군요.

근데 아무리 그래도 템플릿 매개변수는 컴파일 타임에 결정이 되어야 할 텐데, vector의 길이는 런타임에나 결정될 수 있는 것 아니던가요?
이게 되면 정말 제 상식을 뛰어넘겠군요. 파이썬도 아니고 C++에서 런타임에 결정되는 함수 시그너쳐라...

근데 왜 f2가 벡터 매개변수를 받게 작성하시지는 않는 거죠? 그거라면 아주 간단히 될 텐데요.

twinwings의 이미지

저도 처음 보는 물건이었는데,

정식 명칭이 "variadic templates" 이네요.

꼬리-재귀함수 와 비슷한 형태를 컴파일 시간에 완성해주는 물건인가 봅니다.

여기에 자세히 설명되어 있습니다.

http://eli.thegreenplace.net/2014/variadic-templates-in-c/

밑은 위 사이트의 예제에 조금 틀린 게 있어서 수정해보았습니다.

  1. #include <iostream>
  2.  
  3. template<typename T>
  4. bool pair_comparer(T a, T b) {
  5. return a == b;
  6. }
  7.  
  8. template<typename T, typename... Args>
  9. bool pair_comparer(T a, Args... args) {
  10. return a == pair_comparer(args...);
  11. }
  12.  
  13. int main()
  14. {
  15. std::cout << pair_comparer(1, 1) << std::endl;
  16. std::cout << pair_comparer(1, 2) << std::endl;
  17. std::cout << std::endl;
  18.  
  19. std::cout << pair_comparer(1, 1, 1) << std::endl;
  20. std::cout << pair_comparer(1, 1, 2) << std::endl;
  21. std::cout << std::endl;
  22.  
  23. std::cout << pair_comparer(1, 1, 1, 1) << std::endl;
  24. std::cout << pair_comparer(1, 1, 1, 2) << std::endl;
  25. std::cout << std::endl;
  26. }

그나저나, 꺽쇠는 어떻게 해야하죠...

익명 사용자의 이미지

그래도 여전히 파라미터 갯수는 컴파일 타임에 결정이 되는 꼴이죠.
pair_comparer(1, 1, 1, 2)처럼 말입니다. (이 경우엔 최대 4개)

파라미터 갯수 자체가 런타임에서야 결정되는 경우에는 컴파일 타임에 할 수 있는 게 없지 않습니까?
미리 다 만들어 놓을 수도 없고...

twinwings의 이미지

음? 런타임에 가능한 것 처럼 들렸나요?

정확한 스펙은 확인 해 보지 않았지만, 저도 컴파일 타임에만 가능할 것이라 생각합니다.

yudonguk의 이미지

말씀 하신 방법에 대해서 딱 떨어지는 해결법이 떨오르질 않네요. 대신 약간 전제 조건을 수정함으로 써 해결하는 방법을 3가지 작성해 보았습니다.

1. std::vector를 사용하지 않을 경우
std::vector를 반드시 사용해야하는게 아니라면 아래와 같은 방법을 사용할 수 있습니다.

template < class... T >
int f1(int(*f2)(T...), T... v)
{
	return f2(v...);
}

2. std::tuple을 f2의 파라미터로 사용할 경우
std::vector를 쓸수 밖에 없는 상황에서 std::tuple을 이용한다면 아래와 같은 방법도 사용할수 있습니다.

template<typename Tuple, size_t size = std::tuple_size<Tuple>::value>
struct Helper
{
	static void set(Tuple& result, std::vector<void*> const& vec)
	{
		typedef typename std::tuple_element<size - 1, Tuple>::type Element;
		std::get<size - 1>(result) = *static_cast<Element*>(vec[size - 1]);
		Helper<Tuple, size - 1>::set(result, vec);
	}
};
 
template<typename Tuple>
struct Helper<Tuple, 0>
{
	static void set(Tuple& result, std::vector<void*> const& vec)
	{}
};
 
template < class... T >
int f1(int(*f2)(std::tuple<T...> const&), std::vector<void*> const& v)
{
	std::tuple<T...> params;
	Helper<decltype(params)>::set(params, v);
	return f2(params);
}

3. 부수적인 코드를 동반할 수 있을 경우
이 방법이 사용하는 측면에선 말씀하셨던 형태 그대로 사용할 수 있긴하나 부수적인 코드가 많아지는 문제가 있겠네요.

template<typename... T>
struct Helper
{
	typedef std::tuple<T...> Params;
 
	static int call(int(*f2)(T...), std::vector<void*> const& v)
	{
		return callImpl(f2, v, std::integral_constant<size_t, sizeof...(T)>());
	}
 
	static int callImpl(int(*f2)(T...), std::vector<void*> const& v, std::integral_constant<size_t, 0>)
	{
		return f2();
	}
 
	static int callImpl(int(*f2)(T...), std::vector<void*> const& v, std::integral_constant<size_t, 1>)
	{
		return f2(*static_cast<typename std::tuple_element<0, Params>::type*>(v[0]));
	}
 
	static int callImpl(int(*f2)(T...), std::vector<void*> const& v, std::integral_constant<size_t, 2>)
	{
		return f2(*static_cast<typename std::tuple_element<0, Params>::type*>(v[0])
				, *static_cast<typename std::tuple_element<1, Params>::type*>(v[1]));
	}
 
	static int callImpl(int(*f2)(T...), std::vector<void*> const& v, std::integral_constant<size_t, 3>)
	{
		return f2(*static_cast<typename std::tuple_element<0, Params>::type*>(v[0])
				, *static_cast<typename std::tuple_element<1, Params>::type*>(v[1])
				, *static_cast<typename std::tuple_element<2, Params>::type*>(v[2]));
	}
 
	// 이 후 가능한 파라미터 수에 따라 모두 작성해 주어야 합니다.
};
 
template < class... T >
int f1(int(*f2)(T...), std::vector<void*> const& v)
{
	return Helper<T...>::call(f2, v);
}

jehnpark의 이미지

불가능하진 않습니다. 방법을 찾아냈네요

#include <tuple>
#include <vector>
//단순히 가변템플릿의 숫자를 위한 구조체
template<int... S>
struct seq {};
//숫자를 받으면 숫자 수 만큼 가변템플릿을 만드는 구조체
template<int N, int...S>
struct gens :public gens<N - 1, N - 1, S...> {};
template<int... S>
struct gens < 0, S... > { typedef seq < S... > type; };
 
//seq를 통해 {S}를 입력받음
template<class Fn, class T, int... S>
auto tuplecall(Fn f, T t, seq<S...>)->decltype(f(std::get<S>(t)...)) {
	return f(std::get<S>(t)...);
}
//gens를 통해 {T}로 seq를 만든 다음 call함
template<class Fn, class... T>
auto tuplecall(Fn f, std::tuple<T...> t)->decltype(tuplecall(f, t, typename gens<sizeof...(T)>::type())) {
	return tuplecall(f, t, typename gens<sizeof...(T)>::type());
}
//vector를 tuple로 만듬
template<class... T, int... S>
auto vectortotuple(std::vector<void*>& v, seq<S...>)->decltype(std::tie(((nativeclass<T>)(*v[S]))->value...)) {
	return std::tie((*(T*)(v[S]))...);
}
//vector를 tuple로 만듬
template<class... T>
std::tuple<T...> vectortotuple(std::vector<void*>& v) {
	return
		vectortotuple<T...>(v, gens<sizeof...(T)>::type());
}
//vector를 tuple로 만든 다음 call
template<class R, class... T>
auto vectorcall(R(*f)(T...), std::vector<void*>& v)->decltype(tuplecall(f, vectortotuple<T...>(v))) {
	return tuplecall(f, vectortotuple<T...>(v));
}

여러분도 좋은공부가 되셨으면 합니다.

익명 사용자의 이미지

위에 익명 단 사람입니다. 입증할 방법은 없지만요. 그나저나 C++에서 functional programming을 보다니...
TMP의 달인이신 것 같네요. 저는 이 정도 수준의 코드는 읽기도 벅찹니다.

대충 파악하기로는 결국 파라미터의 갯수는 주어진 함수(f)의 타입으로부터 주어지는 것이고(T...),
핵심은 파라미터의 순서에 대응하는 서수들을 제공해주는 가변 템플릿을 만드는 것이었군요.
그걸 만드시느라 gens 구조체를 만드신 거고요.

기막힌 코드 잘 봤습니다. 제가 놓치고 있는 TMP의 세계를 엿볼 수 있었네요. 아 이거 공부해야 되는데 -_-;;;

dkim의 이미지

재미있게 잘 읽었습니다. 참고로, C++11 뿐만 아니라 C++14도 사용가능한 상황이라면, gens를 직접 작성하시는 대신 C++14에 추가된 std::integer_sequence (그리고 그 사용을 편리하게 하는 std::make_index_sequence 등)을 사용하시는 게 편하실 겁니다.

그리고, tuplecall의 역할을 하는 std::apply도 C++17에 추가될 예정이라고 합니다. libstdc++이나 libc++ 최신 버전을 사용 중이시라면 C++17이 나오기 전이라도 std::experimental::apply를 대신 사용할 수 있습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.