2016-05-10 18 views
0

比方說,我有以下的回調類:如何創建抽象類的內聯實現?

class LogCallback { 
public: 
    virtual void sendLog(std::string log) = 0; 
    virtual void setErrorCode(int code) = 0; 
}; 

而且我有它接受一個回調實現發動機:

class Engine { 
public: 
    Engine(); 
    virtual ~Engine(); 
    void setCallback(LogCallback* callback); 
    void start(); 
private: 
    LogCallback* logCallback; 
}; 

現在我可以創建一個實現類:

class OutLogger : public LogCallback { 

    void sendLog(std::string log) { 
     cout << "out: " << log << endl; 
    } 

    void setErrorCode(int code) { 
     sendLog("error code " + std::to_string(code)); 
    } 

}; 

並使用它:

int process() { 
    Engine engine; 
    OutLogger out; 
    engine.setCallback(&out); 
    engine.start(); 
} 

在C++ 11我也可以使用匿名局部類:

int anonymous() { 
    Engine engine; 
    class : public LogCallback { 

     void sendLog(std::string log) { 
      cerr << "err: " << log << endl; 
     } 

     void setErrorCode(int code) { 
      sendLog("error code " + std::to_string(code)); 
     } 

    } err; 
    engine.setCallback(&err); 
    engine.start(); 
} 

現在有這樣一個問題:我可以做同樣沒有明確的匿名類,函數調用裏面?

如果是Java的我會做這樣的:上面

public class AnonymousClass { 

    private abstract class LogCallback { 

     abstract void sendLog(String log); 

     abstract void setErrorCode(int code); 
    } 

    private class Engine { 

     public void setCallback(LogCallback callback) { 
      this.callback = callback; 
     } 

     public void start() { 
      if (callback != null) { 
       callback.sendLog("Starting..."); 
      } 
     } 

     private LogCallback callback; 
    } 

    public void process() { 
     Engine engine = new Engine(); 
     engine.setCallback(new LogCallback() { 
      @Override 
      void sendLog(String log) { 
       System.out.println("out: " + log); 
      } 

      @Override 
      void setErrorCode(int code) { 
       sendLog("error code " + code); 
      } 
     }); 
     engine.start(); 
    } 

    public static void main(String[] args) { 
     AnonymousClass example = new AnonymousClass(); 
     example.process(); 
    } 

} 
+6

簡短的回答是*不,你不能*,因爲據我所知,C++不提供這種功能。 – Holt

+4

使用std :: function。 –

+0

您的Java版本看起來與您的C++版本非常相似,在概念上 –

回答

2

霍爾特的評論是正確的。在諸如Java的new X() { ... }之類的表達式中聲明新類型的唯一方法是lambda表達式,並且不能使其從抽象基類派生。

C++的方法是停止OOP繼承的所有ickiness並使用函數模板,或者刪除類型爲std::function的函數,因此它接受任何具有適當調用簽名的調用,然後您可以使用拉姆達。

例如:

class Engine { 
public: 
    enum class LogType { String, ErrorCode }; 
    using LogCallback = std::function<void(LogType, std::string, int)>; 
    Engine(); 
    virtual ~Engine(); 

    void setCallback(LogCallback callback) { logCallback = callback; } 

    void start() { 
     if (logCallback) 
      logCallback(LogType::String, "Starting...", 0); 
    } 

private: 
    LogCallback logCallback; 
}; 

int anonymous() { 
    Engine engine; 
    engine.setCallback([](Engine::LogType t, std::string log, int code) { 
     if (t == Engine::LogType::ErrorCode) 
      log = "error code " + std::to_string(code); 
     cerr << "err: " << log << endl; 
    }); 
    engine.start(); 
} 

(用於LogCallback A型更好的定義是:

using LogCallback = std::function<void(std::variant<std::string, int>)>; 

,但我們並沒有在C++ variant,只是還沒有)

+0

您能舉一個例子嗎? – Cezariusz

+1

代替'Engine'中的'LogCallback * logCallback',你可以爲'sendLog'和'sendErrorCode'提供2個'std :: function'對象,用作回調函數。 – rozina

+0

@rozina,因爲OP的例子顯示了一個使用另一個的回調函數('setErrorCode'使用'sendLog'),所以有一個回調可以執行兩個任務。 –

1

你不能實現接口,但可以創建帶回調函數的泛型類作爲參數:

class GenericLogCallback : public LogCallback { 
public: 
    GenericLogCallback(std::function<void (std::string)> sendlog, 
    std::function<void (int)> seterrorcode) : sendLog_(std::move(sendlog)), 
    setErrorCode_(std::move(seterrorcode)) {} 

    virtual void sendLog(std::string log) override { 
     if(sendLog_) sendLog_(log); 
    } 
    virtual void setErrorCode(int code) override { 
     if(setErrorCode_) setErrorCode_(code); 
    } 
private: 
    std::function<void (std::string)> sendLog_; 
    std::function<void (int)> setErrorCode_; 
}; 

... 
GenericLogCallback err([](std::string){},[](int){}); 
engine.setCallback(&err); 
+1

最好使用這個而不是'LogCallback',如果你使用'std :: function'隱藏,那麼沒有虛擬調度點。引擎會以此爲價值 –

1

的答案已經張貼在這裏使用std::function是好的,但如果你很在意性能,你可以做一個更好的直接存儲仿函數(這可能是std::bind()結果,或C風格的函數指針,或者lambda表達式):

template <typename SendLog, typename SetErrorCode> 
class GenericLogger : public LogCallback { 
public: 
    GenericLogger(SendLog sender, SetErrorCode setter) 
     : m_sender(sender), m_setter(setter) {} 

    void sendLog(std::string log) override { 
     m_sender(log); 
    } 

    void setErrorCode(int code) override { 
     m_setter(code); 
    } 

    SendLog m_sender; 
    SetErrorCode m_setter; 
}; 

template <typename SendLog, typename SetErrorCode> 
GenericLogger<SendLog, SetErrorCode> makeLogger(SendLog sender, SetErrorCode setter) { 
    return GenericLogger<SendLog, SetErrorCode>(sender, setter); 
} 

void sendLog(std::string log) { 
    std::cout << "out: " << log << std::endl; 
} 

void setErrorCode(int code) { 
    sendLog("error code " + std::to_string(code)); 
} 

int main() 
{ 
    Engine engine; 
    auto out = makeLogger(
     [](std::string s){std::cout << "lambda: " << s << '\n';}, 
     setErrorCode); 
    engine.setCallback(&out); 
    engine.start(); 
} 

使用std::function上面避免除了當向makeLogger()實際參數是該類型的。這通過調用給定的函數來減少開銷,而不是始終存儲std::function

1

有一兩件事你可以做的是創建一個包裝類,允許使用lambda表達式:

template<class F1, class F2> 
struct LogCallbackF : LogCallback { 
    F1 f1; 
    F2 f2; 
    LogCallbackF(F1 f1, F2 f2) : f1(std::move(f1)), f2(std::move(f2)) {} 

    void sendLog(std::string msg) override { f1(msg); } 
    void setErrorCode(int code) override { f2(code); } 
}; 

template<class F1, class F2> 
inline LogCallbackF<F1, F2> make_log_callback(F1 f1, F2 f2) { 
    return {std::move(f1), std::move(f2)}; 
} 

void process() { 
    Engine engine; 
    auto callback = make_log_callback(
     [](std::string a) { std::cout << a << '\n'; }, 
     [](int a) { std::cout << a << '\n'; } 
     ); 
    engine.setCallback(&callback); 
    engine.start(); 
}