객체내의 멤버 변수를 사용하는 함수의 포인터를 어떻게 지칭할

neocoin의 이미지

부딪친 문제에 대한 간단한 예를 작성해 보았습니다.

다음과 같은 소스가 있습니다.
하고 싶은 의도는 comment 해놓은 것과 비슷합니다.
어떻게 해야 할까요?

// 실행 가능한 코드
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

class Element{
public:
	int data;
	Element(int aData){ data = aData; }
};

/// 이 코드(start~end)를 Logic class 내부에 넣고 싶습니다.
// start
int times = 0;
void printE(Element elem){
	times++;
	cout << times << "번째 실행 " << elem.data << endl;
}
// end

class Logic{
public:
	vector<Element> datas;
	Logic(){
		for ( int i= 0; i< 10; i++){
			Element element(i);
			datas.push_back(element);
		}
	}	

	void run(){
		::times = 0;
		for_each(datas.begin(), datas.end(), printE);
	}
};

int main(){
	Logic logic;
	logic.run();
}

즉, Logic class 가 이렇게 되기를 원하는 것이지요.

// 실행 불가능한 코드
class Logic{
public:
	vector<Element> datas;
	Logic(){
		for ( int i= 0; i< 10; i++){
			Element element(i);
			datas.push_back(element);
		}
	}	
	int times;

	void printE(Element elem){
		times++;
		cout << times << "번째 실행 " << elem.data << endl;
	}

	void run(){
		times = 0;
		// this->printE 이것이 불가능합니다.
		for_each(datas.begin(), datas.end(), this->printE);
	}
};

보시는 바와 같이, 상태 유지를 위한 class 내의 변수 times가 필요한 상황
입니다. 그래서, 멤버 함수의 포인터를 넘기고 싶은데, 이것이 힘듭니다.

물론, functor 를 쓰면 어느정도 가능합니다. 다음과 같이 말이지요.

// 제일 첫번째 예제의 Element class 와 main 코드가 있으면 돌아갑니다.
class Logic{
public:
	vector<Element> datas;
	Logic(){
		for ( int i= 0; i< 10; i++){
			Element element(i);
			datas.push_back(element);
		}
	}	

	class PrintE{
	public:
		int times;
		PrintE() : times(0){}
		void operator() (Element elem) {
			this->times++;
			cout << times << "번째 실행" << elem.data << endl;
		}
	};

	void run(){
		for_each(datas.begin(), datas.end(), PrintE());
	}
};

하지만 배보다 배꼽이 더 큰 형상이 되어 버리는것 같습니다.
어떻게 하면 짧고, 명쾌하게 넘길수 있을까요?
서지원의 이미지

굳이, 아래와 같이 하면 printE를 Logic class안으로 넣고, for_each2로 한방에 해결할 수 있기는 합니다.

#include <iostream>
#include <algorithm>
#include <vector>

template <class _InputIter, class _Container, class _Function>
_Function for_each2(_InputIter __first, _InputIter __last, _Container __c, _Function __f) {
      for ( ; __first != __last; ++__first) {
          __f(__c, *__first);
      }
        return __f;
}


using namespace std;

class Element{
public:
   int data;
   Element(int aData){ data = aData; }
};

class Logic{
public:
    vector<Element> datas;
    Logic(){
       for ( int i= 0; i< 10; i++){
          Element element(i);
          datas.push_back(element);
       }
    }

    int times;

    static void printE(Logic *logic, Element elem){
       logic->times++;
       cout << logic->times << "번째 실행 " << elem.data << endl;
    }

   void run(){
         times = 0;
         for_each2(datas.begin(), datas.end(), this, &(Logic::printE));
   }
};

int main(){
   Logic logic;
   logic.run();
}

아시다시피, c++에서는 instance의 method를 호출할 때, instance객체인 this가 implicit하게 첫 parameter로 넘어갑니다.
따라서, template 함수인 for_each 에 함수 pointer만 알려줘서는 제대로 instance의 method를 호출할 수가 없고, instance 자체를 알려줘야 합니다.
하지만 standard template library에는 vector내에 들어있는 instance들에게 적용하는 것만 있으므로, 이 경우 당연히 template를 따로 만들어 줘야 합니다. 그래서 새로 만든 template 함수가 for_each2입니다.

하지만 저같으면 저렇게 하지 않고, 그냥

for(ele = datas.begin(); ele != datas.end(); ele++) {
    printE(ele); 
}

처럼 하겠습니다.[/code]
neocoin의 이미지

'c++에서는 instance의 method를 호출할 때, instance객체인 this가 implicit하게 첫 parameter로 넘어갑니다. '

을 몰랐습니다. 왜? 라는 것에는 답변이 되었습니다.

그리고 뒷부분의

for(ele = datas.begin(); ele != datas.end(); ele++) { 
    printE(ele); 
} 

을 지양하려는 이유는 이것이 의미 전달에 실패하고 있다고 생각해서 입니다.
for_each(datas.begin(), datas.end(), this->printE)
를 사용이 가능하고, 더 나아가

for_each(datas, this->printE) 로 사용한다면, 더 명시적이고(for-each) 짧은 코드(1/3)로 판단되어 가능성을 알아보고 싶었습니다. (물론, 두번째 코드는 STL 알고리즘이 container뿐만 아니라 배열을 수용한다는 면에서 구현이 안되었다는 점이 아깝습니다. 다른 알고리즘들도 때문에 코드가 늘어나지요.)

몇 가지 아이디어에 대한 개인적인 욕심도 있었는데, 다른 가능성을 생각해 봐야 겠습니다. 그리고 왜 CppUnit(http://cppunit.souceforge.net) 가 그런
형태인지 이해가는 계기가 되었습니다. 감사합니다.

clhitter의 이미지

이럴 때마다 functor를 만드는게 번거롭기 때문에 c++ standard library에서는 binder, adapter, negator 라는 놈들을 제공합니다. (더 자세한 것은 stroustrup의 책을 참고하세요)

boost에는 이걸 좀 더 편하게 할 수 있게 해주는 bind라는 놈을 제공하고요
lambda라는 놈도 있지만 vc6에서는 컴파일이 안되기 때문에 저는 주로 boost::bind를 사용합니다.

#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>
#include <boost/bind.hpp>

using namespace std;

class Element
{
public :
  int data;
  Element(int aData) : data(aData) { }
};

class Logic
{
public :
  vector<Element> data;
  Logic()
  {
    for (int i = 0; i < 10; ++i)
      data.push_back(Element(i));
  }

  int times;

  void PrintE(Element elem)
  {
    ++times;
    cout << times << " 번째 실행 " << elem.data << endl;
  }

  void run()
  {
    times = 0;
    for_each(
      data.begin(), data.end(),
      bind1st(mem_fun(&Logic::PrintE), this));
  }

  void run2()
  {
    times = 0;
    for_each(
      data.begin(), data.end(), boost::bind(&Logic::PrintE, this, _1));
  }
};

int main()
{
  Logic logic;
  logic.run();
  logic.run2();
  return 0;
}

안타깝게도 vc6에서 run()은 컴파일이 안되는 군요;;
(mem_fun가 zero parameter인 method에만 적용되고 bind1st는 return type이 void인 function에는 적용되지 않는 것 같습니다. 혹시 피해갈 수 있는 방법 아는 분은 알려주세요)

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.