[英]how to pass lambda express as parameter into mermber function in variadic template class
[英]How to properly pass C strings to a lambda inside a variadic template function
這是我的復雜情況:
我有一個使用variadic模板和lambda的函數:
template<typename...Args>
void foo(Args...args) {
// the arguments are passed to the lambda
_func = [args...](){ do_sth(args...); };
}
在觀察特定事件時,將觸發lambda _func
。 我的問題是,我傳遞的一些參數是C字符串,它們可能指向臨時的std字符串,如下所示:
const char *pStr = __temp_std__string__.c_str();
鑒於我打電話給foo(pStr);
,當_func
時,pStr指向的臨時字符串已被釋放。 我想知道是否存在處理此問題的通用方法。 我正在使用C ++ 11。
編輯:
也許我應該發布我的整個故事,因為你們許多人建議傳遞std :: string而不是C字符串,有理由我無法擺脫它。
我正在使用cocos2d-x開發游戲,它部署了C ++ 11。 我想要做的是當玩家改變他們的語言偏好 (從UI中選擇) 時支持標簽的自動本地化 。
我已將文本保存在幾個文件中,並且每個文件都包含單個語言的本地化文本,它們基本上屬於以下結構:
{
"key1" : "_localized_text1_",
"key2" : "_localized_text2_",
...
}
我的想法是觀察語言偏好變化的事件(通過通知),我會得到一個表示該語言的密鑰,以便從正確的文件中獲取本地化的文本。 以下是我在對象類Label
實現它的方法:
class Label {
// this method would update the label's displayed text
void setString(const std::string& text);
// set a callback for changing language
void setOnLanguageChangeFunc(std::function<void(Notification*)> func);
// set a localised text, which would be updated on changing language
void setLocalizeString(const std::string& key);
};
核心函數是setLocalizeString
(我跳過其他兩個方法的實現,因為它們的聲明足夠直觀):
void Label::setLocalizeString(const std::string& key) {
// the callback lambda
auto callback = [=](Notification *pNotification){
setString(LOCALIZED_STRING(key));
}
// assign the lambda
setOnLanguageChangeFunc(callback);
}
其中LOCALIZED_STRING
是用鍵獲取本地化字符串的宏助手; 並且lambda callback
將在setOnLanguageChangeFunc
保存為Label
本地成員變量。
這在大多數情況下都很有效,使情況變得復雜的是,本地化文本中涉及格式說明符 ,例如:
{
...
"keyN" : "%s eats %d cookies",
...
}
這種格式占位符在代碼中動態傳遞:
// formatStr = "Tom eats 5 cookies"
std::string formatStr = StringKit::stringWithFormat("%s eats %d cookies", "Tom", 5);
其中StringKit
是一個格式化字符串的實用程序,它接受可變參數,這些參數將傳遞給vsnprintf
以產生輸出。 現在你知道為什么我需要傳遞C字符串而不是std :: string,這只是因為格式化字符串的基礎方法。
現在我必須修改Label::setLocalizeString
以便它可以消化可能的可變參數:
template<typename... Args>
void setLocalizeString(const std::string& key, Args... args)
{
// the callback lambda
auto callback = [=](Notification *pNotification){
setString(StringKit::stringWithFormat(LOCALIZED_STRING(sKey), args...));
}
// assign the lambda
setOnLanguageChangeFunc(callback);
}
這是它的用例:
// on changing language, the label would display "Tom eats 5 cookies"
pLabel->setLocalizeString("keyN", "Tom", 5);
這個案例就像一個魅力,因為C字符串參數是全局的,但是當它從臨時的std :: string傳遞時:
std::string tempStr = "Tom";
pLabel->setLocalizeString("keyN", tempStr.c_str(), 5);
由於指向的std :: string已經消失,因此C字符串“Tom”將失去調用lambda回調的值。
我嘗試了幾種方法,比如玩元組的東西,或者在lambda中捕獲一個基本類型的包裝類,但它們都不能解決問題。 但是,我認為應該存在一些棘手的解決方案。
此問題與lambdas或可變參數函數無關 - 如果只是存儲字符串,也會出現此問題:
const char* global_storage;
int main()
{
{
std::string s = "hi";
global_storage = s.c_str();
}
// !!! `global_storage` points to deleted memory!
use(global_storage);
}
你需要確保字符串足夠長。 使用std::string
而不是const char*
是一個很好的起點:
std::string global_storage;
int main()
{
{
std::string s = "hi";
global_storage = std::move(s);
}
// OK, local string was moved into `global_storage`.
use(global_storage.c_str());
}
如果你真的需要使用C風格的字符串,只需將它存儲在lambda / whatever中作為std::string
, 然后在需要使用它時調用.c_str()
,而不是在存儲它時。
在將它存儲在lambda中時,需要將char const*
參數轉換為std::string
。 這是一種可能的方式,我可以建議:
#include <iostream>
#include <tuple>
using namespace std;
template<typename T, typename R = conditional_t<is_same<T, char const*>::value, string, T>>
R bar (T &&value) {return value;}
template<class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch,Tr>& os,
const Tuple & t,
std::index_sequence<Is...>)
{
using swallow = int[]; // guaranties left to right order
(void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...};
}
template<class Ch, class Tr, class... Args>
decltype(auto) operator<<(std::basic_ostream<Ch, Tr>& os,
const std::tuple<Args...>& t)
{
os << "(";
print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
return os << ")";
}
template<typename...Args>
decltype(auto) foo(Args...args)
{
return [args = make_tuple(bar(args)...)] () { cout<< args; return; };
}
int main() {
string *s = new string("Hello, World!");
const char *p = s->c_str();
auto f = foo(1, p, 3.14);
delete s;
f();
return 0;
}
函數foo
返回lambda,它將可變參數存儲為元組,其中每個char const*
元素自動轉換為std::string
。 之后你可以釋放臨時字符串。 現在釋放后釋放lambda應該是安全的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.