1. TupleHelper的主要功能

(1)打印:由于tuple中的元素是可变参数模板,外部并不知道内部到底是什么数据,有时调试时需要知道其具体值,希望能打印出tuple中所有的元素值。

(2)根据元素值获取索引位置:tuple接口中有根据索引位置获取元素的接口,根据元素值来获取索引位置是相反的做法。

(3)获取索引:在运行期根据索引获取索引位置的元素。

(4)遍历tuple:类似于std::for_each算法,可以将函数对象应用于tuple的每个元素

(5)反转tuple:将tuple中的元素逆序。

(6)应用于函数:将tuple中的元素进行一定的转换,使之成为函数的入参

2. 打印tuple

(1)通过模板特化和递归来展开打印tuple

  ①tuple内部的元素个数和类型是不固定的,需要需要用std::get<N>(tuple)来获取元素。

  ②模板特化和递归调用展开tuple的本质就是通过整型模板参数递减来实现的。TuplePrint<decltype(tup), N>::print(tup);通用N来控制递归调用,直到N递减为1.

  ③PrintTuple2(tup)是辅助函数,可以减少外面调用的入参,方面调用TuplePrinter。

(2)通过索引序列来展开并打印tuple

  ①根据tuple的参数Args…,创建一个可变的索引序列:make_index<Args…>::type

  ②根据索引序列来获取tuple中对应位置的元素,并转化为另一种可供print_impl函数使用的参数包:std::get<Indexes>(tup)…

  ③本质上是将tuple通过可变索引序列,转化为一个可变参数模板参数包

【编程实验】tuple的打印

//TpIndex.hpp

#ifndef _TP_INDEXES_H_
#define _TP_INDEXES_H_ namespace TupleHelper
{ //******tuple参数的索引序列****************/ //*************递增整数序列*******************
//第1种方法
template<int...>
struct IndexTuple{}; //前向声明
template<int I, typename IndexTuple, typename... Types>
struct make_indexes_impl; //定义递归函数(产生递增的整数序列)
//各参数:I用于控制递归次数,Indexes:当前己产生的整数序列,T和Types...:将Types包分解T和Types...两部分
template<int I, int... Indexes, typename T, typename... Types>
struct make_indexes_impl<I, IndexTuple<Indexes...>, T, Types...>
{
//由于Types参数包被分解和T和Types...两部分,所以Types...的参数个数会逐渐减少
//同时IndexTuple<Indexs...,I>会在当前Indexes整数序列的后面加上一个递增的I,即IndexTuple<Indexes..., I>
//因此,是一个递增的整数序列
using type = typename make_indexes_impl<I+, IndexTuple<Indexes..., I>, Types...>::type;
}; //递归终止
template<int I, int... Indexes>
struct make_indexes_impl<I, IndexTuple<Indexes...>>
{
using type = IndexTuple<Indexes...>;
}; //类型萃取
//调用方法如:make_indexes<double, char, int>
template<typename... Types>
struct make_indexes : make_indexes_impl<, IndexTuple<>, Types...>
{}; //第2种方法:
template<int N, int... Indexes>
struct make_indexes2 : make_indexes2<N-, N-, Indexes...>{}; //termination condition
template<int... Indexes>
struct make_indexes2<, Indexes...>
{
using type = IndexTuple<Indexes...>;
}; //第3种方法
template<int end, int cur, int... Indexes>
struct make_indexes3 : make_indexes3<end, cur + , Indexes..., cur>{}; //cur == end, the list has been built
template<int end, int... Indexes>
struct make_indexes3<end, end, Indexes...>
{
using type = IndexTuple<Indexes...>;
}; //*************递减整数序列*******************
//前向声明
template<int I, typename IndexTuple, typename... Types>
struct make_indexes_reverse_impl; //产生递减整数序列
template<int I, int... Indexes, typename T, typename... Types>
struct make_indexes_reverse_impl<I, IndexTuple<Indexes...>, T, Types...>
{
using type = typename make_indexes_reverse_impl<I-, IndexTuple<Indexes..., I->, Types...>::type;
}; //递归终止
template<int I, int... Indexes>
struct make_indexes_reverse_impl<I, IndexTuple<Indexes...>>
{
using type = IndexTuple<Indexes...>;
}; //类型萃取
//调用方法如:make_indexes<double, char, int>
template<typename... Types>
struct make_reverse_indexes : make_indexes_reverse_impl<sizeof...(Types), IndexTuple<>, Types...>
{}; } #endif

//TpPrint.hpp

#ifndef _TP_PRINT_H_
#define _TP_PRINT_H_ #include <tuple>
#include <iostream>
#include "TpIndexes.hpp" namespace TupleHelper
{
//*************tuple的打印******************* //第1种方法:根据索引序列打印
template<typename Last>
void print_impl(Last&& last)
{
std::cout << " " << last << std::endl;
} template<typename Head, typename... Tail>
void print_impl(Head&& head, Tail&&... tail)
{
std::cout << " " << head;
print_impl(tail...);
} //万能转换,将tuple转成Args...
template<typename... Args, int... Indexes>
void tuple_print_impl(IndexTuple<Indexes...>, std::tuple<Args...>&& tup)
{
//取出tuple中的每个元素,并转成供print_imple使用的另一种参数包
print_impl(std::forward<Args>(std::get<Indexes>(tup))...);
} //辅助函数
template<typename... Args>
void PrintTuple(const std::tuple<Args...>& tup) //左值版本
{
tuple_print_impl(typename make_indexes<Args...>::type(),
std::tuple<Args...>(tup));
} template<typename... Args>
void PrintTuple(std::tuple<Args...>&& tup) //右值版本
{
tuple_print_impl(typename make_indexes<Args...>::type(),
std::forward<std::tuple<Args...>>(tup));
} //第2种方法:通过模板特化和递归来展开并打印tuple
template<class Tuple, std::size_t N>
struct TuplePrinter
{
static void print(const Tuple& t)
{
TuplePrinter<Tuple, N->::print(t); //深度递归
std::cout << ", " << std::get<N->(t);
}
}; template<class Tuple>
struct TuplePrinter<Tuple, >
{
static void print(const Tuple& t)
{
std::cout << ", " << std::get<>(t);
}
};
//辅助函数
template<typename... Args>
void PrintTuple2(const std::tuple<Args...>& tup)
{
std::cout << "(";
TuplePrinter<decltype(tup), sizeof...(Args)>::print(tup);
std::cout <<")" << std::endl;
} //第3种方法:
template<typename T, int... Indexes>
void print_impl3(const T& tup, IndexTuple<Indexes...>)
{
//利用初始化列表
int a[] = {(std::cout << std::get<Indexes>(tup) << " ",)...};
(void)a;
std::cout << std::endl;
} template<typename... Args>
void PrintTuple3(const std::tuple<Args...>& tup)
{
typedef typename make_indexes<Args...>::type index_type;
print_impl3(tup, index_type());
} } #endif

//TestPrint.cpp

#include <iostream>
#include <tuple>
#include "TpPrint.hpp" using namespace std;
using namespace TupleHelper; int main()
{
//1. tuple的打印
using Tuple = std::tuple<int,short, double, char, string>;
Tuple tp = std::make_tuple(, , 0.5f, 'a', "ok");
PrintTuple(tp);
PrintTuple2(tp);
PrintTuple3(tp); return ;
}
/*输出结果
e:\Study\C++11\27>g++ -std=c++11 test_tuple_helper.cpp
e:\Study\C++11\27>a.exe
1 2 0.5 a ok
(, 1, 2, 0.5, a, ok)
1 2 0.5 a ok
*/

3. 根据元素值获取索引位置

(1)遍历tuple并判断当前元素值是否与给定的值相等,如果相等,则返回当前索引。否则直到遍历终止时仍没找到,则返回-1。

(2)equal_val<N>(tuple, val)用于判断std::get<N>(tuple)是否等于val。

(3)查找是从tuple的最后一个元素开始的,并返回第1个匹配元素的索引位置。

(4)find_index用于递归查找。findIndex是个辅助函数,便于简化调用。

4. 在运行期根据索引位置获取元素

(1)std::get<N>(tuple):用于获取tuple中的第N个元素,但是N只能是个编译期的常量,不能是个变量(如int i)

(2)可以采用映射的方法,将运行期变量“映射”为编译期常量。(见getArgByIndex函数)。通过自增编译期常量k,将k与运行期变量index比较,当两者相等时表示映射成功,这时调用std::get<k>(tuple)来获取第k个元素值。

5. 遍历tuple

(1)先将tuple展开为可变参数模板,然后用展开可变参数模板的方法遍历tuple类

(2)遍历函数:tuple_for_each(func, tup);由于tuple中的元素类型是变化的因此func不能用lambda表达式,需要用一个泛型函数来处理tuple中的元素(见Functor仿函数)。

6. 反转tuple

(1)生成一个逆序的索引序列,目的是为了从最后一个tuple元素开始,将前面的元素一个一个取出来组成一个新的tuple。

(2)tuple_reverse可接受左右值类型的tuple参数对象。

7. 应用于函数

(1)tuple应用于函数的目的:是将tuple展开作为某个函数的入参(可能有多个参数)。

(2)实现思路:先将tuple展开转换为可变参数模板,然后这个可变参数模板应用于某个函数。

(3)本例未实现当函数返回void的情况,这可重载apply_helper函数。

8. 合并tuple

(1)将两个tuple合起来,前一个tuple中的每一个元素为key后一个tuple中的每个元素为value,组成一个pair集合。

(2)利用索引序列,将两个tuple分别展开,再将这些元素组合成一个pair集合。

【编程实验】tupleHelper的综合应用

//TpApply.hpp

#ifndef _TP_APPLY_H_
#define _TP_APPLY_H_ #include <tuple>
#include "TpIndexes.hpp" namespace TupleHelper
{ //1. 根据元素值获取索引位置
//对于可转换的类型,则直接比较
template<size_t N, typename Tuple, typename T>
static typename std::enable_if<std::is_convertible<typename std::tuple_element<N, Tuple>::type, T>::value ||
std::is_convertible<T, typename std::tuple_element<N, Tuple>::type>::value, bool>::type
equal_val(const Tuple& tp, const T& val)
{
return std::get<N>(tp) == val;
} //不能互转的类型,则直接返回false
template<size_t N, typename Tuple, typename T>
static typename std::enable_if<!(std::is_convertible<typename std::tuple_element<N, Tuple>::type, T>::value ||
std::is_convertible<T, typename std::tuple_element<N, Tuple>::type>::value), bool>::type
equal_val(const Tuple& tp, const T& val)
{
return false;
} //根据值查找索引
template<int I, typename T, typename... Args> //I用于控制递归调用的次数,I-1为元素索引位置
struct find_index
{
static int comp(const std::tuple<Args...>& tup, T&& val)
{
using U = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
using V = typename std::tuple_element<I - , std::tuple<Args...>>::type;
bool bflag = std::is_convertible<U, V>::value || std::is_convertible<V, U>::value;
return (bflag && equal_val<I - >(tup, val)) ?
I - : //从tuple最后一个元素开始查找,当前I-1为元素的索引
find_index<I - , T, Args...>::comp(tup, std::forward<T>(val));
}
}; template<typename T, typename... Args>
struct find_index<, T, Args...>
{
static int comp(const std::tuple<Args...>& tup, T&& val)
{
using U = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
using V = typename std::tuple_element<, std::tuple<Args...>>::type;
bool bflag = std::is_convertible<U, V>::value || std::is_convertible<V, U>::value;
//递归终止,如果找到则返回0,否则返回-1
return (bflag && equal_val<>(tup, val)) ? : -;
}
}; //辅助函数,简化调用
template<typename T, typename... Args>
int findIndex(const std::tuple<Args...>& tup, T&& val)
{
return find_index<sizeof...(Args), T, Args...>::comp(tup, std::forward<T>(val));
} //2. 在运行期根据索引位置获取元素
//第1种方法:将编译期常量和运行期变量进行映射
template<size_t k, typename Tuple>
typename std::enable_if<(k==std::tuple_size<Tuple>::value)>::type //void
getArgByIndex(size_t index, const Tuple& tp)
{
throw std::invalid_argument("arg index out of range");
} template<size_t k = , typename Tuple>
typename std::enable_if<(k<std::tuple_size<Tuple>::value)>::type
getArgByIndex(size_t index, const Tuple& tp)
{
if(k == index){
std::cout << std::get<k>(tp)<< " ";
}else{
getArgByIndex<k + >(index, tp);//通过自增k,使得当k==index时输出
}
} //第2种方法:通过逐步展开参数包
void getArgByIndex2(size_t index, std::tuple<>& tp){} template<typename Arg, typename... Args>
void getArgByIndex2(size_t index, std::tuple<Arg, Args...>& tp)
{
if(index < || index >=std::tuple_size<std::tuple<Arg, Args...>>::value)
throw std::invalid_argument("index is not valid"); if(index > )
getArgByIndex2(index-, (std::tuple<Args...>&)tp); //tp的父类为std::tuple<Args...>
//注意:父子类对象内存模型
else
std::cout << std::get<>(tp) << " ";
} template<typename Arg> //特化:当tuple只有一个参数时
void getArgByIndex2(size_t index, std::tuple<Arg>& tp)
{
std::cout << std::get<>(tp) << " ";
} //3. 遍历tuple
template<typename Func, typename Last>
void for_each_impl(Func&& f, Last&& last)
{
f(std::forward<Last>(last));
} template<typename Func, typename First, typename... Rest>
void for_each_impl(Func&& f, First&& first, Rest&&... rest)
{
f(std::forward<First>(first));
for_each_impl(std::forward<Func>(f), std::forward<Rest>(rest)...); //rest为tuple中的各个元素组成的参数包
} template<typename Func, int... Indexes, typename... Args>
void for_each_helper(Func&& f, IndexTuple<Indexes...>, std::tuple<Args...>&& tup)
{
for_each_impl(std::forward<Func>(f),
std::forward<Args>(std::get<Indexes>(tup))...); //将tuple展开为可变参数模板的参数包!
} template<typename Func, int... Indexes, typename... Args>
void for_each_helper(Func&& f, IndexTuple<Indexes...>, std::tuple<Args...>& tup)
{
for_each_impl(std::forward<Func>(f),
std::forward<Args>(std::get<Indexes>(tup))...); //将tuple展开为可变参数模板的参数包!
} //tuple_for_each
template<typename Func, typename... Args>
void tuple_for_each(Func&& f, std::tuple<Args...>& tup)
{
for_each_helper(std::forward<Func>(f),
typename make_indexes<Args...>::type(),
tup);
} template<typename Func, typename... Args>
void tuple_for_each(Func&& f, std::tuple<Args...>&& tup)
{
for_each_helper(std::forward<Func>(f),
typename make_indexes<Args...>::type(),
std::forward<std::tuple<Args...>>(tup));
} //4. 反转tuple
template<class... Args, int... Indexes>
auto reverse_impl(std::tuple<Args...>& tup, IndexTuple<Indexes...>&&) ->
decltype(std::make_tuple(std::get<Indexes>(std::forward<std::tuple<Args...>>(tup))...))
{
return std::make_tuple(std::get<Indexes>(tup)...);
} template<class... Args, int... Indexes>
auto reverse_impl(std::tuple<Args...>&& tup, IndexTuple<Indexes...>&&) ->
decltype(std::make_tuple(std::get<Indexes>(std::forward<std::tuple<Args...>>(tup))...))
{
return std::make_tuple(std::get<Indexes>(std::forward<std::tuple<Args...>>(tup))...);
} template<class... Args>
auto tuple_reverse(std::tuple<Args...>&& tup)->
decltype(reverse_impl(std::forward<std::tuple<Args...>>(tup),typename make_reverse_indexes<Args...>::type()))
{
return reverse_impl(std::forward<std::tuple<Args...>>(tup),typename make_reverse_indexes<Args...>::type());
} template<class... Args>
auto tuple_reverse(std::tuple<Args...>& tup)->
decltype(reverse_impl(std::forward<std::tuple<Args...>>(tup),typename make_reverse_indexes<Args...>::type()))
{
return reverse_impl(tup, typename make_reverse_indexes<Args...>::type());
} //5. tuple应用于函数(将tuple中的元素展开成一个个参数,并传给f函数)。
template<typename F, typename Tuple, int... Indexes>
auto apply_helper(F&& f, IndexTuple<Indexes...>&& in, Tuple&& tup)->
decltype(std::forward<F>(f)(std::get<Indexes>(tup)...))
{
return std::forward<F>(f)(std::get<Indexes>(tup)...);
} template<class F, class... Args>
typename std::result_of<F(Args...)>::type
apply(F&& f, const std::tuple<Args...>& tup)
{
return apply_helper(std::forward<F>(f), typename make_indexes<Args...>::type(), tup);
} //6. 合并tuple
template<std::size_t N, typename T1, typename T2>
using pair_type = std::pair<typename std::tuple_element<N, T1>::type, typename std::tuple_element<N, T2>::type>; template<std::size_t N, typename T1, typename T2>
pair_type<N, T1, T2> pairs(const T1& tup1, const T2& tup2)
{
return std::make_pair(std::get<N>(tup1), std::get<N>(tup2)); //tup1为key, tup2为value
} template<int... Indexes, typename T1, typename T2>
auto pairs_helper(IndexTuple<Indexes...>, const T1& tup1, const T2& tup2)->
decltype(std::make_tuple(pairs<Indexes>(tup1, tup2)...))
{
return std::make_tuple(pairs<Indexes>(tup1, tup2)...);
} template<typename Tuple1, typename Tuple2>
auto Zip(Tuple1 tup1, Tuple2 tup2)->
decltype(pairs_helper(typename make_indexes2<std::tuple_size<Tuple1>::value>::type(), tup1, tup2))
{
static_assert(std::tuple_size<Tuple1>::value == std::tuple_size<Tuple2>::value, "tuples should be the same size.");
return pairs_helper(typename make_indexes2<std::tuple_size<Tuple1>::value>::type(), tup1, tup2);
} }
#endif

//test_tuple_helper.cpp

#include <iostream>
#include <tuple>
#include "TpPrint.hpp"
#include "TpApply.hpp" using namespace std;
using namespace TupleHelper; struct Functor
{
template<typename T>
void operator()(T&& t) const
{
cout << t << " ";
}
}; struct PairFunctor
{
template<typename T>
void operator()(T&& t) const
{
cout << "key = " << t.first << " , value = " << t.second << endl;
}
}; int main()
{
using Tuple = std::tuple<int,short, double, char, string>;
Tuple tp = std::make_tuple(, , 0.5f, 'a', "ok"); //1. 根据元素值获取索引位置
cout << "findIndex(tp, \"ok\")..."<< endl;
int index = findIndex(tp, "ok");
cout << index << endl; //2. 在运行期根据索引位置获取元素
cout << "getArgByIndex(i, tp)..."<< endl;
int len = std::tuple_size<Tuple>::value;
for(int i=; i<len; i++)
{
getArgByIndex(i, tp); //getArgByIndex<0>(i, tp);
}
cout << endl; cout << "getArgByIndex2(i, tp)..."<< endl;
//getArgByIndex(5, tp); //error, 索引超出范围,将抛出异常
for(int i=; i<len; i++)
{
getArgByIndex2(i, tp); //getArgByIndex<0>(i, tp);
}
cout << endl ; cout << "Traverse tuple..."<< endl;
//3. 遍历tuple
tuple_for_each(Functor(), std::make_tuple(,,,'b',2.0));
cout << endl; cout << "tuple_reverse(tp)..."<< endl;
//4. 反转tuple
auto tp2 = tuple_reverse(tp);
PrintTuple(tp2);
PrintTuple(tp); //5. 将tuple应用于函数
cout << "apply..."<< endl;
apply([](int a, int b){cout << a + b << endl; return a + b;}, std::make_tuple(, )); //6. 合并tuple
cout << "merge..."<< endl;
auto tp3 = std::make_tuple<int, short, double, char>(, , 2.5, 'a');
auto tp4 = std::make_tuple<double, short, double, char>(1.5, , 2.5, 'z');
auto mypairs = Zip(tp3, tp4);
tuple_for_each(PairFunctor(), mypairs); return ;
}
/*输出结果
e:\Study\C++11\27>g++ -std=c++11 test_tuple_helper.cpp
e:\Study\C++11\27>a.exe
findIndex(tp, "ok")...
4
getArgByIndex(i, tp)...
1 2 0.5 a ok
getArgByIndex2(i, tp)...
1 2 0.5 a ok
Traverse tuple...
1 3 4 b 2
tuple_reverse(tp)...
ok a 0.5 2 1
1 2 0.5 a ok
apply...
3
merge...
key = 1 , value = 1.5
key = 2 , value = 2
key = 2.5 , value = 2.5
key = a , value = z
*/

第27课 可变参数模板(8)_TupleHelper的更多相关文章

  1. 第26课 可变参数模板(7)_any和variant类的实现

    1. any类的实现 (1)any类: ①是一个特殊的,只能容纳一个元素的容器,它可以擦除类型,可以将何任类型的值赋值给它. ②使用时,需要根据实际类型将any对象转换为实际的对象. (2)实现any ...

  2. 第25课 可变参数模板(6)_function_traits和ScopeGuard的实现

    1. function_traits (1)function_traits的作用:获取函数的实际类型.返回值类型.参数个数和具体类型等.它能获取所有函数语义类型信息.可以获取普通函数.函数指针.std ...

  3. 第24课 可变参数模板(5)_DllHelper和lambda链式调用

    1. dll帮助类 (1)dll的动态链接 ①传统的调用方式:先调用LoadLibrary来加载dll,再定义函数指针类型,接着调用GetProcAddress获取函数地址.然后通过函数指针调用函数, ...

  4. 第23课 可变参数模板(4)_Optional和Lazy类的实现

    1. optional类的实现 (1)optional的功能 ①optional<T>的内部存储空间可能存储了T类型的值,也可能没有.只有当optional被T初始化之后,这个option ...

  5. C++反射机制:可变参数模板实现C++反射

    1. 概要   本文描述一个通过C++可变参数模板实现C++反射机制的方法.该方法非常实用,在Nebula高性能网络框架中大量应用,实现了非常强大的动态加载动态创建功能.Nebula框架在Github ...

  6. C++ 0x 使用可变参数模板类 实现 C# 的委托机制

    #ifndef _ZTC_DELEGATE_H_ #define _ZTC_DELEGATE_H_ #include <vector> #include <functional> ...

  7. c++11 可变参数模板类

    c++11 可变参数模板类 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #inc ...

  8. c++11 可变参数模板函数

    c++11 可变参数模板函数 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #in ...

  9. C++反射机制:可变参数模板实现C++反射(使用C++11的新特性--可变模版参数,只根据类的名字(字符串)创建类的实例。在Nebula高性能网络框架中大量应用)

    1. 概要   本文描述一个通过C++可变参数模板实现C++反射机制的方法.该方法非常实用,在Nebula高性能网络框架中大量应用,实现了非常强大的动态加载动态创建功能.Nebula框架在码云的仓库地 ...

随机推荐

  1. kafka producer 发送消息简介

    kafka 的 topic 由 partition 组成,producer 会根据 key,选择一个 partition 发送消息,而 partition 有多个副本,副本有 leader 和 fol ...

  2. Swiper.js使用及API介绍

    Swiper.js详细使用教程http://www.swiper.com.cn/api/start/2014/1218/140.html

  3. linux c使用socket进行http 通信,并接收任意大小的http响应(一)

    如何进行http通信呢?我们打开任意一个浏览器,按F12,再选择网络,然后打开任意一个网站,我们就可以看到浏览器和网站通信的过程 如下图: 然后,我们任意点击一条记录,可以看到 然后,查找http协议 ...

  4. 微信开发】【Asp.net MVC】-- 微信分享功能

    [微信开发][Asp.net MVC]-- 微信分享功能 2017-01-15 09:09 by stoneniqiu, 12886 阅读, 15 评论, 收藏, 编辑 内嵌在微信中的网页,右上角都会 ...

  5. manjaro配置

    manjaro配置 Table of Contents manjaro配置 系统 一.初次使用 二.安装软件 输入法 emacs samba 三.配置修改 konsole shell颜色 系统 man ...

  6. sticky footer

    html: <div class="detail"> <div class="detail-wrapper"> <div clas ...

  7. 剑指Offer 42. 和为S的两个数字 (其他)

    题目描述 输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的. 输出描述: 对应每个测试案例,输出两个数,小的先输出. 题目 ...

  8. WPF Blend Grid 布局

    这几天都在用blend拖拽界面.我想要的效果是 放大后出现的效果是 但实际出来的效果是放大以后能看到所有的控件,缩小以后窗体就把控件个遮住了.怎么办? 在WPF中提供了9种布局方式,具体Grid,Ca ...

  9. APPicon的生成

    APPIcon 工厂图标的生成:http://icon.wuruihong.com/#/home

  10. Linux搭建禅道

    1.开源版安装包下载(64位的下载64位,32位的选中32位) [root@iZbp~]# wget http://dl.cnezsoft.com/zentao/9.0.1/ZenTaoPMS.9.0 ...