- 论坛徽章:
- 2
|
回复 21# starwing83
怎么前面还说得好好的,是OO的接口脏…… 后面就变成C++的接口脏了呢……
C++提供描述接口的方式多,也就容易被玩坏……
如果只提供黑白两色,再乱搞也只能搞出灰度图像;而C++提供的还不只是三原色,乱搞的程度就不可想象了……
不过无论是黑白两色还是三原色,都有各自的优势与劣势就是了。
你说的这些,有一部分应该是属于接口在源代码层面很好,但在二进制层面脏。
解决办法是将代码编译到用户程序中……
1. 重载函数
(先举几个例子,后面写头文件组织)
- R load(char const* file, size_t len); /* 以C接口提供实现 */
- inline R load(std::string const& file) { return load(filen.c_str(), file.length() ); }
复制代码
- struct X;
- struct Y;
- void f_X(struct X* x);
- void f_Y(struct Y* y);
- inline void f(X* x) { return f_X(x); }
- inline void f(Y* y) { return f_Y(y); }
复制代码 不仅仅是避免f_type,或者type_f这样的无数的名字,更重要的是为用户提供进一步的多态操作提供可能。
至少在C++里,就可以在模板中发挥多态能力了:
- template<typename T>
- void algo(T v) { f(v); g(v); }
- // 返回值有一些问题是因为C++被设计为重载不依靠返回值
- // 如果 f_X,f_Y有返回值也是可以弄的,只是演示代码就复杂了
复制代码 如果是f_X/f_Y/g_X/g_Y上面就没法写。
如果语言设计的好,应该也可以在尽量少写甚至不写代码的情况下立即使用类似virtual那样的多态。
只是C++的这种多态要靠class与继承才能完成……
头文件组织就类似于:
- #pragma once //为了短才写的这个,应该用#ifndef #define #else 的……
- #ifdef __cplusplus
- extern "C" {
- #endif
- R load(char const* file,size_t len);
- struct X;
- struct Y;
- void f_X(struct X* x);
- void f_Y(struct Y* y);
- #ifdef __cplusplus
- }
- inline R load(std::string const& file) { return load(file.c_str(),file.length() ); }
- inline void f(X* x) { return f_X(x); }
- inline void f(Y* y) { return f_Y(y); }
- #endif
复制代码 重载名字是乱,std:: string布局确实未知;但反正与用户是二进制一致的,就没有移植性问题。
2. 类型相关函数
C语言没有template的机制,就只好:
1. 用宏来产生代码
2. 用void*来抹掉类型
宏产生代码就不写了…… 倒不是C++复杂,而是宏复杂…… 最终都是编译到用户代码里。
抹掉类型(包括const修饰等),比如qsort,bsearch之类……
依然是通过C接口提供bsearch:
- void * bsearch ( const void * key, const void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) );
复制代码 类型不在, 而且const进non const出……
- // non-const版本
- template<typename T>
- T* bsearch(T const* k, T* p, size_t n, size_t s)
- {
- struct nested {
- static int cmp(void const* a, void const* b) {
- // cmp在这里重新获取类型
- T const& x = *static_cast<T const*>(a);
- T const& y = *static_cast<T const*>(b);
- return x<y? -1: ( y<x? 1: 0 ) ;
- }
- }; // C++11就不用这么麻烦了……
- void* v = bsearch(k, p, n, s, &nested::cmp /*cmp消去类型*/);
- return static_cast<T*>(v); // 恢复T*
- }
- // const版本
- template<typename T>
- T const* bsearch(T const* k, T const* p, size_t n, size_t s) {
- T* v = const_cast<T*>(p); // 为了转发到上面那个版本
- v = bsearch(k, v, n, s);
- return v; // 恢复 const 限定
- }
复制代码 3. 析构函数
这绝对是好东西啊…… lua还求之不得呢……
其实需要的机制是能注册一些函数在离开作用域(尤其是非局部跳转时、异常,error,coroutine……)时调用。
其实lua的coroutine可以用来实现C++当初被砍掉的恢复语意的异常,缺的就是退出作用域时的保护机制……
只是C++被设计为通过栈上变量的析构函数来注册……
这样的设计可能不是最好的,但至少比没有要来得好吧……
如果你真不喜欢析构函数,还有loki.scopeguard, boost.block_exit什么的来帮你写。
同样是将需要的恢复步骤用C接口提供, 然后利用上面两种库帮你调用。
====== ====== ====== ======
总之, 你想要干净的C接口, C++肯定能做到。
而且还可以在这干净的接口上, 在用户端提供一些辅助。
就是我以前说的:
- by C
- lib <--------> user A by C
- by C++ |
- |----> user B by C++ (lib by C++)
- |
- .....
复制代码 只是:
1. 很多人都不知道这种接口方式 —— 至少坛子里那些喜欢时不时通过贬低C++来获得优越感的人通常都不知道。
以前编译器可能不像现在这么多, 版本问题可能也没现在这么严重, 不太注重user/lib使用不同C++编译器会造成什么情况。
即使现在很多人都不知道,或者知道了也不关心这个问题, 只有你我这种有洁癖的才会……
2. 大部分程序员都被OO玩坏了
C++一开始是从C with class发展起来的, 只有那些个描述接口的方式…… 只有这些东西可以让新手模仿……
但不是人人都能认识到自己模仿的东西的缺点, 特别是在没有比较的情况下。
而且, 这玩意需要的智商确实低, 玩得转, 连非编程人员都可以将面向对象当作谈资
3. 即使知道这种接口方式, 实现起来也是很麻烦的……
|
|