Google Test(GTest)使用方法和源码解析——模板类测试技术分析和应用
写C++难免会遇到模板问题,如果要针对一个模板类进行测试,似乎之前博文中介绍的方式只能傻乎乎的一个一个特化类型后再进行测试。其实GTest提供了两种测试模板类的方法,本文我们将介绍方法的使用,并分析其实现原理。(转载请指明出于breaksoftware的csdn博客)
应用
GTest将这两种方法叫做:Typed Tests和Type-Parameterized Tests。我觉得可能叫做简单模式、高级模式比较通用。先不管这些名字吧,我们看看怎么使用
简单模式(Typed Tests)
首先我们要定义一个模板类。但是为了使用GTest框架,它要继承于Test类
template <typename T>
class TypeTest : public Test {
protected:bool CheckData() {if (typeid(T) == typeid(bool)) {return false;}else {return true;}}
};
我们只实现了一个返回bool类型的模板类型判断函数CheckData。
然后我们使用下列方式定义一个类型,类型的模板参数是我们需要传递给TypeTest的模板类型
typedef testing::Types<int, long> IntegerTypes
接下来我们使用TYPED_TEST_CASE宏注册一个测试用例
TYPED_TEST_CASE(TypeTest, IntegerTypes);
最后我们使用TYPED_TEST_P定义一个测试特例
TYPED_TEST_P(TypeTest, Verify) {EXPECT_TRUE(CheckData());
};
如此我们可以对TypeTest<int>和TypeTest<long>进行测试。我们看下结果
[----------] 1 test from TypeTest/0, where TypeParam = int
[ RUN ] TypeTest/0.Verify
[ OK ] TypeTest/0.Verify (2454 ms)
[----------] 1 test from TypeTest/0 (2455 ms total)[----------] 1 test from TypeTest/1, where TypeParam = long
[ RUN ] TypeTest/1.Verify
[ OK ] TypeTest/1.Verify (4843 ms)
[----------] 1 test from TypeTest/1 (11093 ms total)
高级模式
如果我们还要对TypeTest使用其他类型作为模板参数进行测试,可能要这么写
typedef testing::Types<float, double> FloatTypes;
TYPED_TEST_CASE(TypeTest, FloatTypes); // compile error
但是编译会报错,因为TYPED_TEST_CASE中定义的变量重定义了(之前TYPED_TEST_CASE(TypeTest, IntegerTypes);中定义的)。这个时候我们就要使用高级模式
首先我们需要声明一下测试用例类
TYPED_TEST_CASE_P(TypeTest);
然后使用TYPED_TEST_P定义一个测试实体
TYPED_TEST_P(TypeTest, Verify) {EXPECT_TRUE(CheckData());
};
接下来使用REGISTER_TYPED_TEST_CASE_P注册测试用例
REGISTER_TYPED_TEST_CASE_P(TypeTest, Verify);
最后使用INSTANTIATE_TYPED_TEST_CASE_P宏创建每个测试特例
INSTANTIATE_TYPED_TEST_CASE_P(IntegerCheck, TypeTest, IntegerTypes);
INSTANTIATE_TYPED_TEST_CASE_P(FloatCheck, TypeTest, FloatTypes);
INSTANTIATE_TYPED_TEST_CASE_P(BoolCheck, TypeTest, bool);
上面三行测试了三组类型,其输出是
[----------] 1 test from IntegerCheck/TypeTest/0, where TypeParam = int
[ RUN ] IntegerCheck/TypeTest/0.Verify
[ OK ] IntegerCheck/TypeTest/0.Verify (702 ms)
[----------] 1 test from IntegerCheck/TypeTest/0 (703 ms total)[----------] 1 test from IntegerCheck/TypeTest/1, where TypeParam = long
[ RUN ] IntegerCheck/TypeTest/1.Verify
[ OK ] IntegerCheck/TypeTest/1.Verify (400 ms)
[----------] 1 test from IntegerCheck/TypeTest/1 (403 ms total)[----------] 1 test from FloatCheck/TypeTest/0, where TypeParam = float
[ RUN ] FloatCheck/TypeTest/0.Verify
[ OK ] FloatCheck/TypeTest/0.Verify (451 ms)
[----------] 1 test from FloatCheck/TypeTest/0 (453 ms total)[----------] 1 test from FloatCheck/TypeTest/1, where TypeParam = double
[ RUN ] FloatCheck/TypeTest/1.Verify
[ OK ] FloatCheck/TypeTest/1.Verify (403 ms)
[----------] 1 test from FloatCheck/TypeTest/1 (405 ms total)[----------] 1 test from BoolCheck/TypeTest/0, where TypeParam = bool
[ RUN ] BoolCheck/TypeTest/0.Verify
..\test\gtest_unittest.cc(117): error: Value of: CheckData()Actual: false
Expected: true
[ FAILED ] BoolCheck/TypeTest/0.Verify, where TypeParam = bool (5440 ms)
[----------] 1 test from BoolCheck/TypeTest/0 (6633 ms total)
原理解析
简单模式
我们先从typedef testing::Types<int, long> IntegerTypes;这句开始分析。Types定义非常有意思,我截取部分来看看
struct Types0 {};// Type lists of length 1, 2, 3, and so on.template <typename T1>
struct Types1 {typedef T1 Head;typedef Types0 Tail;
};
template <typename T1, typename T2>
struct Types2 {typedef T1 Head;typedef Types1<T2> Tail;
};template <typename T1, typename T2, typename T3>
struct Types3 {typedef T1 Head;typedef Types2<T2, T3> Tail;
};......template <typename T1 = internal::None, typename T2 = internal::None,typename T3 = internal::None, typename T4 = internal::None,typename T5 = internal::None, typename T6 = internal::None,typename T7 = internal::None, typename T8 = internal::None,typename T9 = internal::None, typename T10 = internal::None,typename T11 = internal::None, typename T12 = internal::None,typename T13 = internal::None, typename T14 = internal::None,typename T15 = internal::None, typename T16 = internal::None,typename T17 = internal::None, typename T18 = internal::None,typename T19 = internal::None, typename T20 = internal::None,typename T21 = internal::None, typename T22 = internal::None,typename T23 = internal::None, typename T24 = internal::None,typename T25 = internal::None, typename T26 = internal::None,typename T27 = internal::None, typename T28 = internal::None,typename T29 = internal::None, typename T30 = internal::None,typename T31 = internal::None, typename T32 = internal::None,typename T33 = internal::None, typename T34 = internal::None,typename T35 = internal::None, typename T36 = internal::None,typename T37 = internal::None, typename T38 = internal::None,typename T39 = internal::None, typename T40 = internal::None,typename T41 = internal::None, typename T42 = internal::None,typename T43 = internal::None, typename T44 = internal::None,typename T45 = internal::None, typename T46 = internal::None,typename T47 = internal::None, typename T48 = internal::None,typename T49 = internal::None, typename T50 = internal::None>
struct Types {typedef internal::Types50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,T41, T42, T43, T44, T45, T46, T47, T48, T49, T50> type;
};template <>
struct Types<internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None> {typedef internal::Types0 type;
};
template <typename T1>
struct Types<T1, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None> {typedef internal::Types1<T1> type;
};
template <typename T1, typename T2>
struct Types<T1, T2, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None, internal::None, internal::None, internal::None,internal::None> {typedef internal::Types2<T1, T2> type;
};
这段代码很长,但是其定义了我们用于罗列类型的结构体Types。它是一个递归定义,即Types3依赖于Types2,Types2依赖于Types1,Types1依赖于Types0……。每个模板类都会将自己模板列表的第一个模板别名为Head,剩下的类型别名为Tail。未来我们将看到这两个类型的使用。
我们再看下TYPED_TEST_CASE的实现
# define TYPED_TEST_CASE(CaseName, Types) \typedef ::testing::internal::TypeList< Types >::type \GTEST_TYPE_PARAMS_(CaseName)
# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_
它只是对该测试用例的参数列表类的type做了一个别名,展开代码就是
typedef ::testing::internal::TypeList< IntegerTypes >::type gtest_type_params_TypeTest_
TypeList是个模板类,它将Types<T>别名为type
template <typename T>
struct TypeList {typedef Types1<T> type;
};
对应于我们的例子就是
struct TypeList<IntegerTypes> {typedef Types1<IntegerTypes> type;
};
于是整体展开就是
typedef ::testing::internal::Types1<IntegerTypes> gtest_type_params_TypeTest_
最后我们看下TYPED_TEST的宏实现。它和之前博文介绍的TEST宏有如下相同之处:
- 定义了私有的虚方法TestBody
- 执行了注册逻辑
- 末尾声明了TestBody函数部分,便于开发者填充测试实体
相同的地方我们就不说了,我们看下不同的
# define TYPED_TEST(CaseName, TestName) \template <typename gtest_TypeParam_> \class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \: public CaseName<gtest_TypeParam_> { \private: \typedef CaseName<gtest_TypeParam_> TestFixture; \typedef gtest_TypeParam_ TypeParam; \virtual void TestBody(); \}; \
它继承于一个模板类,模板类的类名是我们通过TYPED_TEST传入的测试用例类。同时它将父类、模板类进行了别名操作。用我们的例子展开代码即是
template <typename T> class TypeTest_Verify_Test: public TypeTest<T> { private: typedef TypeTest<T> TestFixture; typedef T TypeParam; virtual void TestBody(); };
最后一个傀儡变量被初始化
bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \::testing::internal::TypeParameterizedTest< \CaseName, \::testing::internal::TemplateSel< \GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \GTEST_TYPE_PARAMS_(CaseName)>::Register(\"", ::testing::internal::CodeLocation(__FILE__, __LINE__), \#CaseName, #TestName, 0); \
我们把代码展开
bool gtest_TypeTest_Verify_registered_ GTEST_ATTRIBUTE_UNUSED_ =::testing::internal::TypeParameterizedTest<TypeTest, ::testing::internal::TemplateSel<TypeTest_Verify_Test>, gtest_type_params_TypeTest_>\::Register("", ::testing::internal::CodeLocation(__FILE__, __LINE__), 'TypeTest', 'Verify', 0);
我们看下TypeParameterizedTest类的Register实现
template <GTEST_TEMPLATE_ Fixture, class TestSel, typename Types>
class TypeParameterizedTest {public:// 'index' is the index of the test in the type list 'Types'// specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase,// Types). Valid values for 'index' are [0, N - 1] where N is the// length of Types.static bool Register(const char* prefix,CodeLocation code_location,const char* case_name, const char* test_names,int index) {typedef typename Types::Head Type;typedef Fixture<Type> FixtureClass;typedef typename GTEST_BIND_(TestSel, Type) TestClass;// First, registers the first type-parameterized test in the type// list.MakeAndRegisterTestInfo((std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/"+ StreamableToString(index)).c_str(),StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(),GetTypeName<Type>().c_str(),NULL, // No value parameter.code_location,GetTypeId<FixtureClass>(),TestClass::SetUpTestCase,TestClass::TearDownTestCase,new TestFactoryImpl<TestClass>);// Next, recurses (at compile time) with the tail of the type list.return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>::Register(prefix, code_location, case_name, test_names, index + 1);}
};
这段代码非常重要,我们看到核心函数MakeAndRegisterTestInfo,它将我们测试的对象加入到框架的执行队列中。具体它的原理和实现可以参看《Google Test(GTest)使用方法和源码解析——自动调度机制分析》。
第12行别名了Types::Head为Type。Types是传入的模板类,以我们的例子为例,其传入的就是::testing::internal::Types1<IntegerTypes>。我们在介绍Types模板类时提到过Head别名,它是该模板类第一个模板参数类型。对应于我们的例子就是typedef testing::Types<int, long> IntegerTypes;中的int类型。
第13行使用12行别名的类型,特化了我们传入的测试用例类,即该行对应于
typedef TypeTest<int> FixtureClass;
第14行对测试特例类使用了int类型进行特化
template <GTEST_TEMPLATE_ Tmpl>
struct TemplateSel {template <typename T>struct Bind {typedef Tmpl<T> type;};
};# define GTEST_BIND_(TmplSel, T) \TmplSel::template Bind<T>::type
即对应于TypeTest_Verify_Test<T>::type
typedef typename TypeTest_Verify_Test<T> TestClass;
如此MakeAndRegisterTestInfo函数中的参数就比较明确了。
这段代码中还有个非常重要的一个递归调用
// Next, recurses (at compile time) with the tail of the type list.return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail>::Register(prefix, code_location, case_name, test_names, index + 1);
这次调用,最后一个模板参数传递了Types::Tail。它在编译期间将触发编译器进行类型推导,如同抽丝剥茧般,使用typedef testing::Types<int, long> IntegerTypes;中每个模板类型对TypeParameterizedTest::Register进行特化。从而可以在运行期间可以对每个类型进行注册。
高级模式
我们先看下TYPED_TEST_CASE_P宏的实现
# define TYPED_TEST_CASE_P(CaseName) \static ::testing::internal::TypedTestCasePState \GTEST_TYPED_TEST_CASE_P_STATE_(CaseName)
它定义了一个TypedTestCasePState类型的全局变量,对应于我们例子就是
static ::testing::internal::TypedTestCasePState gtest_typed_test_case_p_state_TypeTest_;
TypedTestCasePState类暴露了AddTestName方法用于保存测试用例和测试特例名 再看下REGISTER_TYPED_TEST_CASE_P宏的实现
# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \namespace GTEST_CASE_NAMESPACE_(CaseName) { \typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \} \static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\__FILE__, __LINE__, #__VA_ARGS__)
它使用TYPED_TEST_CASE_P定义的TypedTestCasePState类对象方法VerifyRegisteredTestNames获取已注册的测试用例名,并别名了一个类型。以上两个宏都是和测试用例名称注册有关。接下来我们看下TYPED_TEST_P的实现
# define TYPED_TEST_P(CaseName, TestName) \namespace GTEST_CASE_NAMESPACE_(CaseName) { \template <typename gtest_TypeParam_> \class TestName : public CaseName<gtest_TypeParam_> { \private: \typedef CaseName<gtest_TypeParam_> TestFixture; \typedef gtest_TypeParam_ TypeParam; \virtual void TestBody(); \}; \static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\__FILE__, __LINE__, #CaseName, #TestName); \} \template <typename gtest_TypeParam_> \void GTEST_CASE_NAMESPACE_(CaseName)::TestName<gtest_TypeParam_>::TestBody()
它和我们之前介绍的TYPED_TEST实现是相似的。不同点是:
- 直接使用传入的测试特例名作为类名
- 调用TYPED_TEST_CASE_P定义的TypedTestCasePState类对象AddTestName对测试用例和测试特例名进行注册
- 将测试特例类和傀儡变量初始化过程控制在一个和测试用例名相关的命名空间中
最后我们看下INSTANTIATE_TYPED_TEST_CASE_P的实现
# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \::testing::internal::TypeParameterizedTestCase<CaseName, \GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \::testing::internal::TypeList< Types >::type>::Register(\#Prefix, \::testing::internal::CodeLocation(__FILE__, __LINE__), \>EST_TYPED_TEST_CASE_P_STATE_(CaseName), \#CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
可以说,套路和简单模式的注册方式是一样的。不一样的是它调用了TypeParameterizedTestCase类的Register,而不是TypeParameterizedTest的Register。还有就是Register的第二个参数是在REGISTER_TYPED_TEST_CASE_P别名的类型。我们看下这个类型的相关代码
# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \namespace GTEST_CASE_NAMESPACE_(CaseName) { \typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \} \
// Template lists of length 1, 2, 3, and so on.template <GTEST_TEMPLATE_ T1>
struct Templates1 {typedef TemplateSel<T1> Head;typedef Templates0 Tail;
};
template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
struct Templates2 {typedef TemplateSel<T1> Head;typedef Templates1<T2> Tail;
};
template <>
struct Templates<NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT> {typedef Templates0 type;
};
template <GTEST_TEMPLATE_ T1>
struct Templates<T1, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT> {typedef Templates1<T1> type;
};
template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2>
struct Templates<T1, T2, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT,NoneT> {typedef Templates2<T1, T2> type;
};
可以见得这个类型和之前的Types是类似的,用于在编译期间通过编译器推导特例出注册方法。需要注意的是这个地方推导的不是模板类的类型,而是测试特例类。我们在讲解TYPED_TEST_P时提过,宏中直接使用传入的测试特例名作为类名,这是有原因的。原因就是在这儿要一个个推导。可能这么说还不太明白,我们看下高级模式的另外一种用法
TYPED_TEST_P(TypeTest, Verify) {EXPECT_TRUE(CheckData());
};TYPED_TEST_P(TypeTest, Verify2) {EXPECT_FALSE(CheckData());
};REGISTER_TYPED_TEST_CASE_P(TypeTest, Verify, Verify2);
这儿的Verify和Verify2都是测试特例名,于是通过REGISTER_TYPED_TEST_CASE_P操作后,就变成
typedef ::testing::internal::Templates<Verify,Verfiy2>::type gtest_AllTests_;
最后在下面注册函数中,触发对该函数使用Verify和Verfiy2进行特化的操作。
template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types>
class TypeParameterizedTestCase {public:static bool Register(const char* prefix, CodeLocation code_location,const TypedTestCasePState* state,const char* case_name, const char* test_names) {std::string test_name = StripTrailingSpaces(GetPrefixUntilComma(test_names));if (!state->TestExists(test_name)) {fprintf(stderr, "Failed to get code location for test %s.%s at %s.",case_name, test_name.c_str(),FormatFileLocation(code_location.file.c_str(),code_location.line).c_str());fflush(stderr);posix::Abort();}const CodeLocation& test_location = state->GetCodeLocation(test_name);typedef typename Tests::Head Head;// First, register the first test in 'Test' for each type in 'Types'.TypeParameterizedTest<Fixture, Head, Types>::Register(prefix, test_location, case_name, test_names, 0);// Next, recurses (at compile time) with the tail of the test list.return TypeParameterizedTestCase<Fixture, typename Tests::Tail, Types>::Register(prefix, code_location, state,case_name, SkipComma(test_names));}
};
在TypeParameterizedTestCase类的Register方法中我们看到有对TypeParameterizedTest的Register的调用,于是两种方式打通了。一个测试特例下的类型推导是在TypeParameterizedTest的Register中完成的,而测试用例下不同测试特例的推导则在TypeParameterizedTestCase类的Register方法中完成的。
相关文章:

IT人才职场受宠
面对就业压力的日益增大,就业难,工资水平低等问题困扰着所有的大学生。然而,IT业的迅猛发展却造成了数以万计的网络设计、运行、维护的网络工程师需求的空缺,巨大的人才缺口使得IT业“全线告急”,这也促使更多的研究人…

引用头文件#include queue出错
为什么80%的码农都做不了架构师?>>> 在工程头文件中引用头文件 #include <queue> 莫名奇妙出错,其原因很可能是由于头文件引用问题。 include/c/4.7.1/bits/stl_vector.h:1308:40: error: expected unqualified-id before ‘(’ token…

ZOJ1002 Fire Net(非递归版)
以前用递归的回溯搜索思路做过一次,参见ZOJ1002 Fire Net(递归版),今天想着用非递归的方法试试看,呵呵,比我想象中要难啊,主要还是堆栈里究竟放什么,这一点上思路一直没理清。因此用了整整一天的时间&#…
“数学不行,干啥也不行”骨灰级程序员:其实你们都是瞎努力
编程圈一直都流传着一个段子:一流程序员靠数学,二流程序员靠算法,末端程序员靠百度,低端看高端就是黑魔法。懂的人其实都知道,这不是段子,其实就是程序员的真实写照。想一想,我们日常学习、求职…

Google Test(GTest)使用方法和源码解析——死亡测试技术分析和应用
死亡测试是为了判断一段逻辑是否会导致进程退出而设计的。这种场景并不常见,但是GTest依然为我们设计了这个功能。我们先看下其应用实例。(转载请指明出于breaksoftware的csdn博客) 死亡测试技术应用 我们可以使用TEST声明并注册一个简单的测…
java学习笔记11--Annotation
java学习笔记11--Annotation Annotation:在JDK1.5之后增加的一个新特性,这种特性被称为元数据特性,在JDK1.5之后称为注释,即:使用注释的方式加入一些程序的信息。 java.lang.annotation Annotation接口是所有的Annotat…

GoogleLog(GLog)源码分析
GLog是Google开发的一套日志输出框架。由于其具有功能强大、方便使用等特性,它被众多开源项目使用。本文将通过分析其源码,解析Glog实现的过程。 该框架的源码在https://github.com/google/glog上可以获取到。本文将以目前最新的0.3.3版本源码为范例进行…

Ajax Toolkit 控件学习系列(13) ——FilteredTextBoxExtender 控制输入
这个控件的作用是对TextBox所要输入的内容进行过滤控制。按照自己需要过滤,可以自定义,再或者使用定义好的方式。看效果。效果不是很突出,说明下,就是只能输入大写字母和数字。因为加了限制,但是具体有什么高深的应用呢…
Uber最新开源Manifold,助力机器学习开发者的可视化与调试需求
所有参与投票的 CSDN 用户都参加抽奖活动群内公布奖项,还有更多福利赠送作者 | Lezhi Li译者 | 凯隐编辑 | Jane出品 | AI科技大本营(ID:rgznai100)【导语】2019 年 1 月,Uber 推出了 Manifold,一款与模型无…

jQuery对象和DOM对象使用说明
1.jQuery对象和DOM对象第一次学习jQuery,经常分辨不清哪些是jQuery对象,哪些是 DOM对象,因此需要重点了解jQuery对象和DOM对象以及它们之间的关系.DOM对象,即是我们用传统的方法(javascript)获得的对象,jQuery对象即是用jQuery类库…

[WPF疑难]避免窗口最大化时遮盖任务栏
[WPF疑难]避免窗口最大化时遮盖任务栏 周银辉 WPF窗口最大化时有个很不好的现象是:如果窗口的WindowStyle被直接或间接地设置为None后(比如很多情况下你会覆盖默认的窗体样式,即不采用Windows默认的边框和最大化最等按钮,来打造个…

Google Mock(Gmock)简单使用和源码分析——简单使用
初识Gmock是之前分析GTest源码时,它的源码和GTest源码在同一个代码仓库中(https://github.com/google/googletest)。本文我将以目前最新的Gmock1.7版本为范例,分析其实现原理。(转载请指明出于breaksoftware的csdn博客…
浪潮刘军:为什么说计算力是AI时代“免费的午餐”?
出品 | AI科技大本营(ID:rgznai100)产业AI、元脑生态是浪潮集团2019年度的两大关键词。作为一家以计算力为核心生产力的企业,浪潮还一直强调人工智能计算是未来最重要的计算力,而无论产业AI、元脑生态都构筑于计算的基础设施之上。…

Journey源码分析四:url路由
2019独角兽企业重金招聘Python工程师标准>>> 在入口函数main()的default分支中,对路由进行了注册,现在分析下。 ##main()中路由注册相关代码 源码: httpRouter : httptreemux.New() // Blog and pages as http server.InitializeBlog(httpRou…
“天河二号”总工程师杜云飞谈星光超算应用平台设计
整理 | 夕颜出品 | AI科技大本营(ID:rgznai100)【导读】12 月 21-22 日,OpenI/O 启智开发者大会在深圳召开。在大会上, 国家超级计算广州中心总工程师、“天河二号”总工程师杜云飞发表了题为《星光超算应用平台》的主题报告&…

Google Mock(Gmock)简单使用和源码分析——源码分析
源码分析 通过《Google Mock(Gmock)简单使用和源码分析——简单使用》中的例子,我们发现被mock的相关方法在mock类中已经被重新实现了,否则它们也不会按照我们的期待的行为执行。我们通过阅读源码,来分析整个过程的实现逻辑。(转载…

远程控制软件VNC教程和对内网机器控制的实现
网络遥控技术是指由一部计算机(主控端)去控制另一部计算机(被控端),而且当主控端在控制端时,就如同用户亲自坐在被控端前操作一样,可以执行被控端的应用程序,及使用被控端的系统资源…

GRUB2相关概念
GNU GRUB,简称“GRUB”,是一个来自GNU项目的启动引导程序。GRUB是多启动规范的实现,它允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统。GRUB可用于选择操作系统分区上的不同内核,…
朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型
做Linux网络开发,一般绕不开标题中几种网络编程模型。网上已有很多写的不错的分析文章,它们的基本论点是差不多的。但是我觉得他们讲的还不够详细,在一些关键论点上缺乏数据支持。所以我决定好好研究这几个模型。(转载请指明出于b…
支付宝账单出来后,除了总消费,你看到你的学习支出了吗?
当支付宝的2019年年度账单出来的时候很多人的第一反应就是我怎么花了这么多钱不少人都有这样的困惑忙忙碌碌一年到头,到底得到了什么?而这一切又和自己最开始的规划一样吗?其实从账单上可以看出你在2019年花费了哪些大头居家生活、穿衣打扮还…

体验Windows 7的Superbar
随着PDC 2008上Windows 7 M3 6801的发布,这个微软的下一代操作系统也渐渐浮出了水面。对于我们这些普通的PC用户而言,Windows 7相对于Windows Vista最显而易见的改变,无疑就是著名的Superbar任务栏了。说起它相比于原来的任务栏变化ÿ…

Linux 安装图形界面及远程连接
#可查询哪些组件是否已经安装(可用来对照组件名称) yum grouplistyum groupinstall X Window System -y #安装GNOME桌面环境 yum groupinstall GNOME Desktop Environment -y #安装KDE桌面环境 yum groupinstall KDE (K Desktop Environment)卸载 卸载GNOME桌面环境…
朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型
在《朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型》中我们分析了朴素模型的一个缺陷——一次只能处理一个连接。本文介绍的Select模型则可以解决这个问题。(转载请指明出于breaksoftware的csdn博客) 和朴素模型一样,我们首先…
微软开源NAS算法Petridish,提高神经网络迁移能力
作者 | Jesus Rodriguez译者 | Rachel编辑 | 夕颜出品 | AI科技大本营(ID:rgznai100) 【导读】神经架构搜索(Neural architecture search, NAS)是深度学习中最热门的趋势之一。NAS方法针对特定问题和数据…

[转]g++ 编译多个相关文件
三个文件,一个头文件,两个程序文件 /*d.h */#include <iostream>usingnamespacestd; classDataset { public: intgetdata(); }; /*d.cpp */#include "d.h"intDataset::getdata() { return1231; } /*out.cpp */#include <ios…

POJ--2391--Ombrophobic Bovines【分割点+Floyd+Dinic优化+二分法答案】最大网络流量
联系:http://poj.org/problem?id2391 题意:有f个草场,每一个草场当前有一定数目的牛在吃草,下雨时它能够让一定数量的牛在这里避雨,f个草场间有m条路连接,每头牛通过一条路从一点到还有一点有一定的时间花…
25年了,我总结出这些信息提取的经验教训
作者 | Ehud Reiter译者 | 夕颜出品 | AI科技大本营(ID:rgznai100)【导读】近日,本文作者阿伯丁大学计算科学系教授 Ehud Reiter 及其带领的阅读小组读了一篇让他们印象深刻的论文——由 Ralph Grishman 发表的《信息提取 25 年》(…
朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll模型
在《朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型》中,我们分析了它只能支持1024个连接同时处理的原因。但是在有些需要同时处理更多连接的情况下,1024个连接往往是不够的,也就是不能够高并发。那么这个时候我们就可以采用…

flashcom中远程共享对象SharedObject的用法
觉得这篇文章比较好,转载回来。学习fcs也有差不多一个月了,感觉最有特色的东西还是SharedObject.SharedObject有不少东西,本地操作就不说了(相信很多人没接触fcs也用过);就说说远程共享对象吧.基本的应用流程是:my_nc new NetConnection(); my_nc.connect("rt…

Hive-1.2.0学习笔记(一)安装配置
鲁春利的工作笔记,好记性不如烂笔头下载hive:http://hive.apache.org/index.htmlHive是基于Hadoop的一个数据仓库工具,提供了SQL查询功能,能够将SQL语句解析成MapReduce任务对存储在HDFS上的数据进行处理。MySQ安装Hive有三种运行…