命令行通配符

这是因为 shell 中的通配符(*)展开行为不同:

  1. unzip *.zip
  • shell 会先展开 *.zip,如果当前目录有多个 zip 文件
  • 命令会变成 unzip file1.zip file2.zip file3.zip
  • 这可能导致 unzip 命令收到多个参数而报错
  1. unzip "*.zip"
  • 引号阻止了 shell 展开通配符
  • 通配符会直接传递给 unzip 命令
  • unzip 命令自己处理通配符匹配

正确的写法有几种:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 方法1:使用引号
unzip "*.zip"

# 方法2:使用转义
unzip \*.zip

# 方法3:使用循环处理多个文件
for zip in *.zip; do
unzip "$zip"
done

# 方法4:使用find命令
find . -name "*.zip" -exec unzip {} \;

如果需要更健壮的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

# 解压所有zip文件
unzip_all() {
local zip_files=( *.zip )

# 检查是否存在zip文件
if [ ! -e "${zip_files[0]}" ]; then
echo "没有找到zip文件"
return 1
}

# 逐个解压
for zip in "${zip_files[@]}"; do
echo "解压: $zip"
if ! unzip "$zip"; then
echo "解压失败: $zip"
return 1
fi
done
}

正则与通配符区别

正则表达式(Regular Expressions)和通配符(Wildcards)都是用于模式匹配的工具,但它们在功能、语法和应用场景上有显著的区别。以下是它们之间的主要区别:

1. 定义

  • 正则表达式:
    • 正则表达式是一种用于描述字符串模式的强大工具,允许复杂的匹配规则。它可以用于查找、替换和验证字符串。
  • 通配符:
    • 通配符是一种简单的模式匹配符号,通常用于文件名匹配或简单的字符串匹配。它们的功能相对较简单。

2. 语法

  • 正则表达式:

    • 正则表达式使用特定的语法来定义模式,包括字符类、量词、边界匹配、分组等。例如:
      • ^abc:匹配以 “abc” 开头的字符串。
      • \d:匹配任何数字字符。
      • .*:匹配任意字符(包括空字符)零次或多次。
  • 通配符:

    • 通配符通常使用简单的符号来表示匹配模式:
      • *:匹配零个或多个字符。
      • ?:匹配一个字符。
      • []:匹配括号内的任意一个字符(例如,[abc] 匹配 “a”、”b” 或 “c”)。

3. 功能

  • 正则表达式:

    • 正则表达式功能强大,支持复杂的匹配规则、分组、反向引用、替换等操作。可以用于文本处理、数据验证、搜索引擎等多种场景。
  • 通配符:

    • 通配符功能相对简单,主要用于文件系统中的文件名匹配(如在命令行中使用)或简单的字符串匹配。它们不支持复杂的匹配规则。

4. 应用场景

  • 正则表达式:

    • 常用于编程语言(如 Python、Java、JavaScript、C++ 等)中的字符串处理、数据验证(如电子邮件、电话号码格式)、文本搜索和替换等。
  • 通配符:

    • 常用于文件系统操作(如在命令行中查找文件)、数据库查询(如 SQL 中的 LIKE 操作)等。

5. 示例

  • 正则表达式示例:

    1
    ^[A-Za-z0-9]+@[A-Za-z]+\.[A-Za-z]{2,}$
    • 这个正则表达式用于匹配电子邮件地址。
  • 通配符示例:

    • *.txt:匹配所有以 .txt 结尾的文件。
    • file?.jpg:匹配 file1.jpgfile2.jpg 等文件,但不匹配 file10.jpg

总结

  • 正则表达式 是一种功能强大的模式匹配工具,适用于复杂的字符串处理和验证。
  • 通配符 是一种简单的模式匹配符号,主要用于文件名匹配和简单的字符串匹配。

根据具体的需求选择合适的工具,可以更有效地完成字符串匹配和处理任务。

std::nth_element实现

std::nth_element 是 C++ 标准库中的一个算法,用于在一个范围内重新排列元素,使得第 n 个元素位于其最终位置,并且该元素左侧的所有元素都小于或等于它,右侧的所有元素都大于或等于它。这个算法的时间复杂度为 O(n) 平均情况下,最坏情况下为 O(n^2)。

实现原理

std::nth_element 的实现通常基于快速选择算法(Quickselect),这是一个选择算法,类似于快速排序。其基本思路如下:

  1. 选择一个基准元素: 从数组中选择一个基准元素(pivot)。
  2. 分区: 将数组分为两部分:小于基准元素的部分和大于基准元素的部分。
  3. 递归选择: 根据基准元素的位置与 n 的关系,决定在左侧还是右侧继续查找。

代码实现

以下是一个简单的 std::nth_element 的实现示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib> // for std::rand

// Partition function for Quickselect
template <typename RandomIt>
RandomIt partition(RandomIt first, RandomIt last, RandomIt pivot) {
std::iter_swap(pivot, last - 1); // Move pivot to end
RandomIt storeIndex = first;

for (RandomIt it = first; it < last - 1; ++it) {
if (*it < *(last - 1)) {
std::iter_swap(it, storeIndex);
++storeIndex;
}
}
std::iter_swap(storeIndex, last - 1); // Move pivot to its final place
return storeIndex;
}

// Quickselect function
template <typename RandomIt>
void quickselect(RandomIt first, RandomIt last, size_t n) {
if (first < last) {
RandomIt pivot = first + std::rand() % (last - first); // Random pivot
pivot = partition(first, last, pivot);

if (pivot - first == n) {
return; // Found the nth element
} else if (pivot - first > n) {
quickselect(first, pivot, n); // Search in the left part
} else {
quickselect(pivot + 1, last, n - (pivot - first + 1)); // Search in the right part
}
}
}

// nth_element implementation
template <typename RandomIt>
void my_nth_element(RandomIt first, RandomIt nth, RandomIt last) {
size_t n = nth - first;
quickselect(first, last, n);
}

int main() {
std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
size_t n = 5; // We want the 5th element (0-based index)

my_nth_element(vec.begin(), vec.begin() + n, vec.end());

std::cout << "The " << n << "th element is: " << vec[n] << std::endl;

return 0;
}

代码解释

  1. Partition Function:

    • partition 函数将数组分为两部分,返回基准元素的最终位置。
    • 它将基准元素移动到数组的末尾,然后遍历数组,将小于基准的元素移动到左侧。
  2. Quickselect Function:

    • quickselect 函数递归地选择基准元素并进行分区,直到找到第 n 个元素。
  3. My Nth Element Function:

    • my_nth_element 是用户定义的函数,调用 quickselect 来找到第 n 个元素。
  4. Main Function:

    • main 函数中,创建一个整数向量,调用 my_nth_element,并输出第 n 个元素。

总结

std::nth_element 的实现基于快速选择算法,能够高效地找到数组中第 n 个元素。上述代码展示了如何实现这一算法,并提供了一个简单的示例来演示其用法。

texelFetch

texelFetch 是一个在着色器编程中用于从纹理中获取特定纹素(texel)的函数。与常规的纹理采样不同,texelFetch 使用整数纹理坐标来直接访问纹理数据,不进行过滤或插值。

在 OpenGL 的 GLSL(OpenGL Shading Language)中,texelFetch 的用法如下:

1
vec4 texelFetch(sampler2D sampler, ivec2 coord, int lod);
  • sampler:纹理采样器。
  • coord:整数形式的纹理坐标。
  • lod:细节层次(level of detail),通常为 0。

texelFetch 适用于需要精确访问纹理像素的场合,比如在一些计算或图像处理效果中。

traits设计和用法

在 C++ 中,Traits(特性)是一种设计模式,通常用于提供类型信息或行为的模板类。Traits 允许在编译时获取类型的特性,从而实现更灵活和可扩展的代码。Traits 模式广泛应用于标准库和现代 C++ 编程中,尤其是在模板编程和泛型编程中。

1. Traits 的设计

Traits 通常是一个模板类,专门用于提供与类型相关的信息。它们可以用于:

  • 类型特性: 提供类型的属性(如是否是指针、是否是类等)。
  • 类型转换: 提供类型的转换信息(如获取类型的基类、去除引用等)。
  • 类型操作: 提供与类型相关的操作(如获取类型的大小、默认构造函数等)。

2. Traits 的基本用法

以下是一些常见的 Traits 用法示例:

a. 类型特性

使用 std::is_integral 来检查一个类型是否是整数类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <type_traits>

template<typename T>
void checkType() {
if (std::is_integral<T>::value) {
std::cout << "T is an integral type." << std::endl;
} else {
std::cout << "T is not an integral type." << std::endl;
}
}

int main() {
checkType<int>(); // 输出: T is an integral type.
checkType<double>(); // 输出: T is not an integral type.
return 0;
}

b. 自定义 Traits

你可以定义自己的 Traits 类来提供特定类型的信息。例如,定义一个 Traits 类来获取类型的大小:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

template<typename T>
struct TypeTraits {
static const size_t size = sizeof(T);
};

int main() {
std::cout << "Size of int: " << TypeTraits<int>::size << std::endl; // 输出: Size of int: 4
std::cout << "Size of double: " << TypeTraits<double>::size << std::endl; // 输出: Size of double: 8
return 0;
}

c. 结合 SFINAE

Traits 可以与 SFINAE(Substitution Failure Is Not An Error)结合使用,以实现更复杂的模板特化。例如,选择性地启用某些函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <type_traits>

template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type
process(T value) {
std::cout << "Processing integral type: " << value << std::endl;
}

template<typename T>
typename std::enable_if<!std::is_integral<T>::value>::type
process(T value) {
std::cout << "Processing non-integral type: " << value << std::endl;
}

int main() {
process(42); // 输出: Processing integral type: 42
process(3.14); // 输出: Processing non-integral type: 3.14
return 0;
}

3. Traits 的应用

Traits 在 C++ 标准库中有广泛的应用,以下是一些常见的例子:

  • std::iterator_traits: 提供迭代器的类型信息,如值类型、指针类型等。
  • std::numeric_limits: 提供数值类型的特性,如最小值、最大值等。
  • std::enable_if: 用于条件性地启用模板特化。

4. 总结

  • Traits 是一种强大的设计模式,允许在编译时获取类型信息和行为。
  • 它们可以用于类型特性、类型转换和类型操作,提供灵活性和可扩展性。
  • Traits 在 C++ 标准库中有广泛的应用,尤其是在模板编程和泛型编程中。

通过使用 Traits,开发者可以编写更通用和可重用的代码,同时提高类型安全性和性能。

shell和子shell环境变量

在 Shell 脚本中,命令替换(command substitution)是指将命令的输出作为字符串插入到另一个命令中。命令替换通常使用反引号(`command`)或 $() 语法来实现。命令替换会在一个子 Shell 中执行指定的命令,这意味着在子 Shell 中定义的变量不会影响父 Shell 中的变量。

1. 子 Shell 的概念

  • 子 Shell: 当你在 Shell 中执行一个命令替换时,Shell 会创建一个新的子 Shell 来执行该命令。子 Shell 是父 Shell 的一个独立实例,具有自己的环境和变量。

2. 变量作用域

  • 在子 Shell 中定义的变量不会影响父 Shell 中的变量。相反,父 Shell 中的变量也无法在子 Shell 中被访问。

3. 示例

以下是一个示例,展示了命令替换如何在子 Shell 中运行命令,并且如何影响变量的作用域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash

# 定义一个变量
var="Hello from parent shell"

# 使用命令替换
result=$(echo $var)

# 输出结果
echo "Result from command substitution: $result"

# 在子 Shell 中修改变量
result=$(echo "Hello from child shell"; var="Hello from child shell")

# 输出结果
echo "Result after child shell: $result"
echo "Variable in parent shell: $var"

4. 运行结果

如果你运行上述脚本,输出将是:

1
2
3
Result from command substitution: Hello from parent shell
Result after child shell: Hello from child shell
Variable in parent shell: Hello from parent shell

5. 解释

  • 第一部分:

    • var="Hello from parent shell" 定义了一个变量 var 在父 Shell 中。
    • result=$(echo $var) 使用命令替换,将 var 的值传递给 result。此时,result 的值为 Hello from parent shell
  • 第二部分:

    • result=$(echo "Hello from child shell"; var="Hello from child shell") 在子 Shell 中执行。虽然在子 Shell 中修改了 var 的值,但这个修改不会影响父 Shell 中的 var 变量。
    • result 的值被设置为 Hello from child shell,但父 Shell 中的 var 仍然保持为 Hello from parent shell

6. 总结

命令替换会在子 Shell 中执行命令,因此在子 Shell 中定义的变量不会影响父 Shell 中的变量。这种行为是 Shell 的一个重要特性,理解这一点对于编写有效的 Shell 脚本非常重要。

分支预测

  1. 宏定义解释
    1
    2
    #define likely(x) __builtin_expect(!!(x), 1)
    #define unlikely(x) __builtin_expect(!!(x), 0)
  • likely(x):
    这个宏用于表示某个条件 x 是“可能为真”的。它的作用是告诉编译器,x 很可能为真(即返回值为 1)。
    __builtin_expect(!!(x), 1) 的作用是将 x 的值转换为布尔值(0 或 1),并告诉编译器这个值很可能是 1。
  • unlikely(x):
    这个宏用于表示某个条件 x 是“可能为假”的。它的作用是告诉编译器,x 很可能为假(即返回值为 0)。
    __builtin_expect(!!(x), 0) 的作用是将 x 的值转换为布尔值,并告诉编译器这个值很可能是 0。
  1. 使用场景
    这些宏通常用于条件语句中,以优化分支预测。例如:
1
2
3
4
5
if (likely(condition)) {
// 处理条件为真的情况
} else {
// 处理条件为假的情况
}

在这个例子中,使用 likely 可以帮助编译器优化代码生成,使得在条件为真的情况下,相关的代码路径更快。

  1. 优化原理
    分支预测:现代 CPU 通常具有分支预测机制,能够根据历史执行路径预测条件语句的结果。通过使用 likely 和 unlikely,开发者可以显式地告诉编译器和 CPU 哪个分支更可能被执行,从而提高预测的准确性。
    性能提升:在性能敏感的代码中,减少错误的分支预测可以显著提高执行效率,尤其是在循环和频繁调用的函数中。

  2. 注意事项
    可读性:虽然使用这些宏可以提高性能,但过度使用可能会影响代码的可读性。应在性能关键的部分使用,而不是在所有条件判断中使用。
    编译器支持:__builtin_expect 是 GCC 和 Clang 的扩展,其他编译器可能不支持此功能。在使用时需要确保代码的可移植性。
    总结
    likely 和 unlikely 宏通过利用 __builtin_expect 提供了对条件分支的预测信息,帮助编译器生成更高效的代码。它们在性能敏感的应用中非常有用,尤其是在需要优化分支预测的场景中

c++模版函数delete防止隐士类型转换.md

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Person {
int a = 0;
operator int&() { return a; }
operator const int&() const { return a; }
};

template <typename T>
void testFunc(T arg) = delete;

void testFunc(int arg)
{
printf("called testFunc int\n");
}

int main() {

Person a;
testFunc(a); //报错
return 0;
}

AndoirdEGLContext销毁管理

1
2
3
4
public interface EGLContextFactory {
EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
}

EGLContext 如果释放时,会删除在当前Context 下创建的bufffer,texture 以及fbo等资源吗

在 OpenGL ES 中,当一个 EGLContext 被销毁时,所有在该上下文中创建的 OpenGL 资源(如 buffer、texture 和 framebuffer objects (FBO) 等)都会被删除。这是因为这些资源是与特定的 OpenGL 上下文相关联的,一旦上下文被销毁,与之相关的所有资源也会被自动释放。

具体来说,当你调用 EGLContextFactory 接口的 destroyContext 方法来销毁一个 EGLContext 时,OpenGL ES 会自动处理这个上下文的清理工作,包括删除所有在该上下文中创建的资源。

因此,你不需要手动删除这些资源,销毁上下文的过程会自动处理它们的释放。

不过,如果你需要提前释放某些资源以便在上下文还没有销毁之前释放内存,你可以在合适的时机手动删除这些资源(例如通过调用 glDeleteBuffersglDeleteTexturesglDeleteFramebuffers 等方法)。

从包含平移、旋转和缩放的矩阵中提取仅包含平移和旋转的矩阵

要从包含平移、旋转和缩放的矩阵中提取仅包含平移和旋转的矩阵,您需要对原矩阵进行一些操作。原矩阵通常是一个3x3或4x4的矩阵。

对于3x3矩阵:

原3x3矩阵的形式如下:

1
2
3
| a  b  tx |
| c d ty |
| 0 0 1 |

步骤:

  1. 提取旋转部分(左上2x2子矩阵):

    1
    2
    | a  b |
    | c d |
  2. 计算缩放因子(可以使用行向量的范数或列向量的范数):

    1
    s = sqrt(a^2 + c^2) = sqrt(b^2 + d^2)
  3. 将旋转部分归一化:

    1
    2
    | a/s  b/s |
    | c/s d/s |
  4. 构建新的3x3矩阵,包含平移分量:

    1
    2
    3
    | a/s  b/s  tx |
    | c/s d/s ty |
    | 0 0 1 |

对于4x4矩阵:

原4x4矩阵的形式如下:

1
2
3
4
| a  b  c  tx |
| d e f ty |
| g h i tz |
| 0 0 0 1 |

步骤:

  1. 提取旋转部分(左上3x3子矩阵):

    1
    2
    3
    | a  b  c |
    | d e f |
    | g h i |
  2. 计算缩放因子(可以使用行向量的范数或列向量的范数):

    1
    2
    3
    sx = sqrt(a^2 + d^2 + g^2)
    sy = sqrt(b^2 + e^2 + h^2)
    sz = sqrt(c^2 + f^2 + i^2)
  3. 将旋转部分归一化:

    1
    2
    3
    | a/sx  b/sy  c/sz |
    | d/sx e/sy f/sz |
    | g/sx h/sy i/sz |
  4. 构建新的4x4矩阵,包含平移分量:

    1
    2
    3
    4
    | a/sx  b/sy  c/sz  tx |
    | d/sx e/sy f/sz ty |
    | g/sx h/sy i/sz tz |
    | 0 0 0 1 |

这些步骤可以帮助您从包含平移、旋转和缩放的矩阵中提取仅包含平移和旋转的矩阵。