C++에서 클래스 내 멤버 함수를 콜백 함수로 넘기고 싶을땐 어떻게 하는게 좋나요?

greathero의 이미지

class A {
public:
  void printStatus();
  void msgPrint(string msg);
 
  void testCallback();
};
 
class B {
public:
  void foo(void (*callback1)(), void (*callback2)(string));
  ...
};
 
void A::testCallback() { 
  B *b;
  b->foo(&A::printStatus, &A::msgPrint); // 이렇게 두개의 함수를 콜백으로 넘기고 싶은데 안됩니다.
}

A의 printStatus 함수와 msgPrint 함수를 b의 foo 함수에 콜백으로 넘기고 싶습니다.
어떻게 해야하나요?

익명 사용자의 이미지

void foo(void (A::*callback1)(), void (A::*callback2)(string))

라고 해야 할 듯

greathero의 이미지

void foo(void (A::*callback1)(), void (A::*callback2)(string)) {
  callback2("foo~~"); // 이렇게 호출하면 "명백한 호출의 괄호 앞에 오는 식에는 함수(포인터)형식이 있어야 합니다."라는 신택스 에러가 뜨네요.
}

foo 함수안에서 콜백 호출은 어떻게 해야되는걸까요?;;

그리고 저렇게 함수 시그니처를 scope operator를 써서 한정짓는 방법은 별로 좋지 않아보여서ㅠ

xylosper의 이미지

멤버함수 포인터는 선언방법도 호출방법도 일반적인 함수 포인터와 다릅니다. 이유는 멤버 함수를 호출하기 위해서는 반드시 객체가 필요하기 때문입니다. 멤버 함수 포인터로 검색해보세요.

greathero의 이미지

xylosper님께선 언제나 답변을 상세하고 정확하게 달아주시네요.
덕분에 우선은 콜백 함수 관련 이슈를 해결하였습니다 ㅎㅎ

a287848의 이미지

void printStatus();
void msgPrint(string msg);

를 static 으로 쓰시는건 어떠신지요.

static type으로 callback은 가능합니다.

Dig it.

greathero의 이미지

그렇게 와닿지는 않는 방법이네요ㅠ

kukyakya의 이미지

일반적인 함수도 콜백함수로 받고 멤버함수도 콜백으로 받고 싶으시거든 그냥 C++11에 추가된 std::function을 쓰시는 게 편합니다.

#include <iostream>
#include <string>
#include <functional>
 
struct B {
  template<typename F1, typename F2>
  void foo(F1 &&__f1, F2 &&__f2) {
    f1_ = std::forward<F1>(__f1);
    f2_ = std::forward<F2>(__f2);
  }
 
  void run() {
    f1_();
    f2_("Hello");
  }
 
  std::function<void()> f1_;
  std::function<void(const std::string&)> f2_;
};
 
struct A {
  void print_status() const { std::cout << "A::print_status()" << std::endl; }
  void msg_print(const std::string& __str) const { std::cout << "A::msg_print() with arg " << __str << std::endl; }
};
 
 
int main()
{
  A a;
  B b;
 
  b.foo(std::bind(&A::print_status, &a), std::bind(&A::msg_print, &a, std::placeholders::_1));
  b.run();
}
greathero의 이미지

덕분에 잘 적용했구요~ㅎ

궁금한게 콜백을 register하는 함수의 인자(foo의 인자)를 rvalue로 정한건 rvalue-reference를 통한 "완벽한 전달"을 위해서였나요?

kukyakya의 이미지

네 맞습니다. 그런데 다시 보니 perfect forwarding이 필요가 없었네요. 다음과 같이 move semantic을 활용하시는 것이 가독성을 위해 좋을 것으로 보입니다.

#include <iostream>
#include <string>
#include <functional>
 
struct B {
  typedef std::function<void()> f1_type;
  typedef std::function<void(const std::string&)> f2_type;
 
  void foo(f1_type __f1, f2_type __f2)
  {
    f1_ = std::move(__f1);
    f2_ = std::move(__f2);
  }
 
  void run() {
    f1_();
    f2_("Hello");
  }
 
  f1_type f1_;
  f2_type f2_;
};
 
struct A {
  void print_status() const { std::cout &lt;< "A::print_status()" &lt;< std::endl; }
  void msg_print(const std::string& __str) const { std::cout &lt;< "A::msg_print() with arg " &lt;< __str &lt;< std::endl; }
};
 
 
int main()
{
  A a;
  B b;
 
  b.foo(std::bind(&A::print_status, &a), std::bind(&A::msg_print, &a, std::placeholders::_1));
  b.run();
}
hahaite의 이미지

안녕하세요.

KLDP 5년만에 답변이란 걸... ㅋㅋ

아래와 같이 코드를 짜 보았습니다.
원하는 답변이 될지 모르겠네요.

참고로, foo 함수에 class A 의 객체를 넘겨주는 포인터를 추가하였습니다.

#include <cstdio>
#include <string>
 
using namespace std ;
 
class A 
{
public:
	void printStatus();
	void msgPrint(string msg);
 
	void testCallback();
};
 
class B 
{
public:
	void foo(A* pa, void (A::*callback1)(), void (A::*callback2)(string));
};
 
 
void A::printStatus()
{
	printf("call printStatus()\n") ;
	return ;
}
 
void A::msgPrint(string msg)
{
	printf("call msgPrint() : %s\n", msg.c_str()) ;
	return ;
}
 
void A::testCallback() 
{ 
	B b;
	b.foo(this, &A::printStatus, &A::msgPrint); 
}
 
 
void B::foo(A* pa, void (A::*callback1)(), void (A::*callback2)(string))
{
	string st = "String Test" ;
	(pa->*callback1)() ;
	(pa->*callback2)(st) ;
 
	return ;
}
 
 
int main(void)
{
	A aa ;
	aa.testCallback() ;
 
	return 0;
}

^^

은정의 이미지

감사드립니다

Necromancer의 이미지

C++을 컴파일한 후의 결과물을 보게 되면 메소드들은 고정된 위치에 있는 하나의 "함수"가 되고, 인수 목록에 현재 조작중인 인스턴스에 대한 포인터가 인수 목록에 추가됩니다. 물론 이 인수는 C++ 코드상에는 없는, 컴파일러가 알아서 추가하는 인수입니다.

이때문에 static이 아닌 메소드의 포인터를 콜백함수로 넘겨서 외부에서 호출하면 이 숨겨진 인수 때문에 뭔 일이 터질지 모릅니다.

static method이면 "클래스 내의 유일"이 되므로 일반 메소드처럼 컴파일러가 인스턴스 포인터를 추가할 필요가 없습니다. (특정 인스턴스를 조작해야 한다면 사용자가 직접 인수로 추가해줘야 함)

다만 static method가 함수보다 나은 점이라면 클래스 내에 소속되어 있기 때문에 컴파일러가 더 강하게 에러체크할 수 있습니다. 프로그램 만들 때 그만큼 사람의 실수를 더 줄일수 있는거죠.

Written By the Black Knight of Destruction

댓글 달기

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