玩模板元編程走火入魔是一種怎樣的體驗?
其實寫起來和普通程序差不多啦~
用來玩俄羅斯方塊.
簡單介紹下, 每編譯一次遊戲就會進行一步.
操作是在編譯選項 -D 後面填相應的命令, 比如移動方塊, 旋轉, 下降到底等. 詳情參見GitHub - mattbierner/Super-Template-Tetris: Tetris as a C++ Template Metaprogram體驗就是——被黑出翔了
我寫了一個庫叫template.scala,把C++的模板元編程搬到Scala上了。
微博上的人是這樣黑我的(https://twitter.com/yogthos/status/839180878760771584):C++標準委員會也玩模板走火入魔,但是他們覺得這個門檻實在是太高了,應該讓寫編譯器的人去解決,讓全世界人民都可以低門檻玩轉模板元編程,於是就有了帶SFINAE和constexpr的函數。
於是現在就有了兩種C++編譯器
1、沒實現SFINAE+constexpr函數的編譯器2、號稱實現了但是沒弄對的編譯器(逃有的程序,運行不運行已經無所謂了。
--------
update見過最喪心病狂的應該是模版parser,完全跑在編譯時的那種,可以實現類型安全的printf。
至於編譯時如何處理字元串,可以看這個:https://github.com/hczhcz/reflectionpp/blob/master/reflection%2B%2B/static_str.hpp
順便,這個項目里另一個走火入魔的東西是,實現了一個編譯時mutable的變數。-&> 輸出素數表-&> 編譯期生成素數表-&> 編譯錯誤信息生成素數表
#include &
template &
struct Check;
template &
struct Check&
{
enum { value = Check&::value };
};
template &
struct Check&
{
enum { value = false };
};
template &
struct Check&
{
enum { value = true };
};
template&
//struct Prime
//{
// enum { is_prime = Check&::value };
//}
;
template&
struct Prime&
{
enum { is_prime = false };
};
template &
struct PrintPrime
{
enum
{ count = PrintPrime&::count +
(Prime&::value&>::is_prime ? 1 : 0)
};
PrintPrime()
{
PrintPrime&();
if (Prime&::value&>::is_prime)
{
printf(" No.%d : %d
", count, i);
}
}
};
template &<&>
struct PrintPrime&<1&>
{
enum { count = 0 };
PrintPrime() {}
};
int main()
{
PrintPrime&<0x1f3&>(); // 499
}
在vs下,大概會得到這樣的信息:
…………………省略一堆………………
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<397,true&>」
1&> main.cpp(41): note: 參見「Prime&<397,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<401,true&>」
1&> main.cpp(41): note: 參見「Prime&<401,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<409,true&>」
1&> main.cpp(41): note: 參見「Prime&<409,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<419,true&>」
1&> main.cpp(41): note: 參見「Prime&<419,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<421,true&>」
1&> main.cpp(41): note: 參見「Prime&<421,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<431,true&>」
1&> main.cpp(41): note: 參見「Prime&<431,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<433,true&>」
1&> main.cpp(41): note: 參見「Prime&<433,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<439,true&>」
1&> main.cpp(41): note: 參見「Prime&<439,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<443,true&>」
1&> main.cpp(41): note: 參見「Prime&<443,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<449,true&>」
1&> main.cpp(41): note: 參見「Prime&<449,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<457,true&>」
1&> main.cpp(41): note: 參見「Prime&<457,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<461,true&>」
1&> main.cpp(41): note: 參見「Prime&<461,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<463,true&>」
1&> main.cpp(41): note: 參見「Prime&<463,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<467,true&>」
1&> main.cpp(41): note: 參見「Prime&<467,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<479,true&>」
1&> main.cpp(41): note: 參見「Prime&<479,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<487,true&>」
1&> main.cpp(41): note: 參見「Prime&<487,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<491,true&>」
1&> main.cpp(41): note: 參見「Prime&<491,true&>」的聲明
1&>main.cpp(41): error C2027: 使用了未定義類型「Prime&<499,true&>」
1&> main.cpp(41): note: 參見「Prime&<499,true&>」的聲明
源於 Erwin Unruh 大神的 Primzahlen Original
——————————通過我不斷改那個數字日編譯器,我發現vs2015最多(默認)只能被日到499,500他就受不了了= = ┑( ̄Д  ̄)┍另,把注釋去掉就能運行了
題目說的是元編程,但好多回答的是泛型編程,既然如此,那我也強大下吧。
1:accumulation-dev/src/rpc at master · IronsDu/accumulation-dev · GitHubrpc庫,註冊服務,無需處理反序列化和序列化(也就是無需手動組包和解析參數)(參數類型支持int,map,string,vector,tuple,protobuf,當然也就包括它們之間的任意組合)(目前底層用的序列化是RapidJson或MsgPack)
(其中很多地方,我個人覺得是我個人的模板技術的上限了)(斯認為模板不是去記住書上的規則,其實重要記性好,很容易記住。而是要知道怎麼/何時/合適的利用模板去解決問題,要充分發揮想像力~~這點就很難了)
伺服器:static int add(int a, int b)
{
//CenterServerRPCMgr::getRpcFromer()為調用者會話對象
return a + b;
}
static void addNoneRet(int a, int b, dodo::RpcRequestInfo reqInfo)
{
// 添加dodo::RpcRequestInfo reqInfo形參(不影響調用者調用)
// 這裡本身不返回數據(函數返回類型為void),但RPC本身是具有返回值語義的
// 適用於需要調用其他非同步操作之後(通過reqInfo)才能返回數據給調用者的情況
// 譬如:
/*
auto caller = CenterServerRPCMgr::getRpcFromer();
redis-&>get("k", [caller, reqInfo](const std::string value){
caller-&>reply(reqInfo, value);
});
*/
/*
//客戶端:
centerServerConnectionRpc-&>call("test", 1, 2, [](const std::string value){
});
*/
}
void initCenterServerExt()
{
CenterServerRPCMgr::def("test", [](int a, int b){
return a + b;
});
CenterServerRPCMgr::def("testNoneRet", [](int a, int b){
});
CenterServerRPCMgr::def("add", add);
CenterServerRPCMgr::def("addNoneRet", addNoneRet);
gLogicCenterServerClient-&>call("add", 1, 2);
gLogicCenterServerClient-&>call("add", 1, 2, [](int result) {
}); // 最後一個參數可以是function類型,作為非同步回調(處理伺服器返回值),且咱不會把它作為參數發送給伺服器。
想把boost庫全部重新寫一遍.....我已經重寫了boost的一部分了......GitHub - yufengzjj/ATMPL: Another Template Meta Programming Library
模板元編程其實就是寫lisp嘛。。。感受就是調試超級痛苦(就算用的是clang)
GitHub - Cheukyin/TemplatedPL: A Simple Functional Programming Language Interpreter Based On C++ Template
這是我之前用模板元編程寫過的一個解釋器,可以定義高階函數,支持閉包和continuation,在編譯期對表達式做CPS變換,所有結果都由編譯器推導出來可正因為所有都是編譯器推導,根本就無法單步調試,
像下面這種超級複雜的模板嵌套,一旦寫錯一點,編譯器就會拋出幾十噸的error:新建.cpp,條件反射template&<而不再是int main
想要編譯期 debugger
默默翻出多年前的代碼, 現在已經不敢讀了...
最大的體驗是微軟家的編譯器好脆弱...編譯代碼一處錯誤報上上百行是幸運的情況, 畢竟是有的看...Fatal Error C1001 次之, 好歹能試出來哪裡出的問題, 然後再連猜帶蒙的繞過這個bug...最坑的是直接崩潰... 一按F7, 幾秒鐘之後VS彈個框告訴你編譯器掛掉了...PROMISE_TEMPLATE_LIST
template &
auto PROMISE::then(OnFulfilled on_fulfilled, OnRejected on_rejected,
OnProgress on_progress, Attachments... attachments)
-&> std::enable_if_t&<
_::ContinuationTypeTrait&
Category&
using namespace _;
using ResultType = typename ContinuationTypeTrait&<
T, OnFulfilled, OnRejected, OnProgress, Attachments...&>::ResultType;
using ResultPromise = Category&
auto default_on_fulfilled = [](ObjectifyVoid&
return returnMaybeVoid(std::move(v));
};
using OnFulfilledType = decltype(forwardMaybeNull&
std::forward&
auto default_on_rejected = [](std::exception_ptr e) {
std::rethrow_exception(e);
return returnMaybeVoid(
std::move(*reinterpret_cast&
};
using OnRejectedType = decltype(forwardMaybeNull&
std::forward&
auto default_on_progress = [](ObjectifyVoid&
using OnProgressType = decltype(forwardMaybeNull&
std::forward&
auto old_node = node_.get();
auto new_node = ContinuationTypeTrait&
NodeMaker&
old_node-&>getEventLoop(), std::move(node_),
std::forward_as_tuple(std::forward&
auto new_node_ptr = new_node.get();
old_node-&>setOnReadyCallback(partiallyApply(
[new_node_ptr](OnFulfilledType on_fulfilled, OnRejectedType on_rejected,
NodeType* node) {
auto result = node-&>getResult();
if (result.isException())
transformAndFulfill(new_node_ptr, on_rejected, result.getException());
else
transformAndFulfill(new_node_ptr, on_fulfilled, result.getValue());
},
forwardMaybeNull&
std::move(default_on_fulfilled)),
forwardMaybeNull&
std::move(default_on_rejected))));
old_node-&>setOnProgressCallback(
partiallyApply([new_node_ptr](OnProgressType on_progress, NodeType* node,
ObjectifyVoid&
try {
callContinuation(on_progress, std::move(value),
new_node_ptr-&>getAttachment&<0&>());
}
catch (...) {
new_node_ptr-&>setException(std::current_exception());
}
},
forwardMaybeNull&
std::forward&
std::move(default_on_progress))));
return ResultPromise{
std::unique_ptr&
}
PROMISE_TEMPLATE_LIST
template &
auto PROMISE::then(OnFulfilled on_fulfilled)
-&> std::enable_if_t&<
_::ContinuationTypeTrait&
Category&
return then(std::forward&
default_on_progress_t{});
}
PROMISE_TEMPLATE_LIST
template &
auto PROMISE::then(OnFulfilled on_fulfilled, OnRejected on_rejected)
-&> std::enable_if_t&<
_::ContinuationTypeTrait&
Category&
return then(std::forward&
std::forward&
}
好多年沒寫template,不過還依稀記得當年元編程那本神書《Modern C++ Design》不得不在前言里附一個編譯器的連接,因為只有這個實驗室的前沿編譯器才能編譯書里的代碼,雖然這些例子都符合C++標準。據說現在情況也沒有特別好轉。。
我對C++模板真的挺恐懼的,只知道最簡單的用在STL裡面的模板。不知道這段代碼算不算模板元編程:
出自G家程序員之手,聽說這個庫在G家程序員手裡應該是人手一份,願意是根據某個函數的參數類型自動生成一個回調函數,用於註冊事件的回調函數之用。老實說,這樣的C++代碼,國內C++程序員能hold住的真不多,即使能hold住,gcc對模板的編譯出錯提示也很不友好,單查問題就能把你看哭了。
就我一個更崇尚C的程序員來看,還不如使用這樣的虛基類,基類中定義消息的類型和虛處理函數,收到消息時根據消息類型來查找註冊的回調函數呢:
class Message {
int type_;
virtual int Handle() = 0;
Message(int type) : type_(type) {
}
virtual ~Message();
}
噗,我來說一個Meta-Meta-Programming,用Racket寫marco來生成C++的Template。http://matt.might.net/papers/ballantyne2014metameta.pdf
玩過一段時間,作為編程可玩性來說,不錯。但是作為生產工具來說,性價比太低。
玩的時候寫的代碼現在已經不想改了.最大的感受就是編譯器和智能感知真脆弱.調試真痛苦
說實話,玩了幾年之後感覺太傷腦,智力遊戲罷了,能不用就不用了。
定義類時,敲的第一個字母是 t
模板元就是,想實現A語法,但是模板沒有直接A的東西,然後輾轉構造BCDEFG類型,然後一個套一個終於實現了A語法,然後感覺運行起來快了那麼一丟丟,好滿足呀。
推薦閱讀: