小编Pat*_*ght的帖子

哪些 GCC 优化标志对二进制大小影响最大?

我正在使用 GCC 为 ARM 开发 C++。我遇到了一个问题,我没有启用优化,我无法为我的代码创建二进制文件(ELF),因为它不适合可用空间。但是,如果我只是启用调试优化(-Og)(据我所知这是可用的最低优化),代码就很容易适应。

在这两种情况下,都会启用-ffunction-sections-fdata-sections-fno-exceptions-Wl,--gc-sections 。

  • 闪存大小:512 kB
  • 没有优化:.text 溢出约 200 kB
  • 使用-Og优化:.text 约为 290 kB

即使进行了最小的优化,二进制大小也存在巨大差异。

我查看了3.11 控制优化的选项,详细了解使用 -Og 标志执行哪些优化,看看这是否会给我任何见解。

哪些优化标志对二进制大小影响最大?我应该寻找什么来解释这种巨大的差异吗?

c++ gcc arm code-size compiler-optimization

29
推荐指数
2
解决办法
9484
查看次数

了解 C++ 特性并使其高效

我最近遇到了“特质”这个有趣且强大的概念,并尝试在 C++ 中理解/实现它们。据我了解,特征提供了一种方法,既可以扩展/调整现有代码的功能,又可以为类定义“接口”,而无需使用传统继承(以及随之而来的所有开销/问题)。我还看到这个概念似乎与C++中的CRTP设计模式密切相关。

举个例子,我用 C++ 编写接口的正常思维过程是定义一个具有纯虚方法的类。然后我可以创建它的子类并将指针传递给我的所有通用代码。然而我发现这有一些问题:

  1. 需要从多个接口继承的类需要使用多重继承,这可能会变得非常复杂并引入“钻石模式”问题。
  2. 形成严格的“是”关系,但这并不总是意图。例如,如果我描述光的接口,则模拟光并不是真正的光,它仅具有“特征”/像光一样起作用。我的意思是,通用 Light 接口并不真正具有实现需要继承的共性,它只是定义了实现应该如何表现。
  3. 虚拟方法和继承允许完全动态多态性,这会产生不必要的开销。在我的大多数代码中,我一次只会使用接口的单个​​实现,因此我不需要动态选择正确的实现,我只需要让接口的“用户”足够通用对于不同的实现。

下面是一个简单、传统的 Light 界面示例:

class Light {
public:
    virtual void on() = 0;
    virtual void off() = 0;
};

class MyLight : public Light {
public:
    void on() override;
    void off() override;
};

void lightController(Light& l) {
    l.on();
    l.off();
}
Run Code Online (Sandbox Code Playgroud)

并且(基于此处的文章: https: //chrisbranch.co.uk/2015/02/make-your-c-interfaces-trait-forward/)这是我认为的“基于特征”的实现相同的概念:

template<typename T>
class Light {
public:
    Light(T& self) : _self(self) {}

    void on() { _self.on(); }
    void off() { _self.off(); }

private:
    T& _self;
};

class …
Run Code Online (Sandbox Code Playgroud)

c++ interface traits

10
推荐指数
1
解决办法
7599
查看次数

使用另一个模板重载可变参数模板函数

我试图弄清楚如何使用“更专业”的可变参数函数模板“重载”可变参数函数模板。例如:

#include <iostream>

template <typename... ARGS_>
void foo(void(*fn)(ARGS_...)) {
    std::cout << "Generic function pointer foo" << std::endl;
}

struct Test {
    
};

template <typename... ARGS_>
void foo(void(*fn)(ARGS_..., Test*)) {
    std::cout << "Test function pointer foo" << std::endl;
}


void test1(int a, int b) {
    std::cout << "test1()" << std::endl;
}

void test2(int a, int b, Test* x) {
    std::cout << "test2()" << std::endl;
}

int main() {
    foo(&test1);
    foo(&test2);
    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这段代码的输出是:

Generic function pointer foo
Generic function pointer …
Run Code Online (Sandbox Code Playgroud)

c++ templates

6
推荐指数
1
解决办法
93
查看次数

从模板类的 std::tuple 中提取类型列表

假设我有以下课程

class Example {
public:
    using value_type = std::tuple<
        uint8_t,
        uint8_t,
        uint16_t
    >;

private:
    value_type _value;
};
Run Code Online (Sandbox Code Playgroud)

现在,我希望能够基于此类型创建另一个类,将每个类类型包装在另一种类型中。基于将每种类型包装在模板化类中的可变参数模板中,我知道我可以通过以下方式实现一半的目标:

template <typename T>
class Wrapper;

template <typename ... ARGS>
class ExampleWrapper {
private:
    std::tuple<Wrapper<ARGS>...> _args;
};
Run Code Online (Sandbox Code Playgroud)

但是,我无法弄清楚的是,ARGS如果我所知道的就是ExampleT在哪里,如何获取TExample。我希望能够使用ExampleWrapper如下:

ExampleWrapper<Example> myWrapper;
Run Code Online (Sandbox Code Playgroud)

c++ templates stdtuple

6
推荐指数
1
解决办法
1135
查看次数

创建前 N 位设置的编译时常量

在 C++17 中,是否有办法在编译时生成前 N 位设置的常量?

在伪代码中,我正在寻找类似的东西:

constexpr uint32_t MY_CONSTANT = setBits<2>();
Run Code Online (Sandbox Code Playgroud)

相当于:

constexpr uint32_t MY_CONSTANT = 0b11;
Run Code Online (Sandbox Code Playgroud)

换句话说,给定编译时常量 N,返回编译时常量 M,其中位 0 到 (N-1) 为 1(设置)。

c++ c++17

3
推荐指数
1
解决办法
126
查看次数

根据特征专门化模板类

我试图弄清楚如何根据模板的结果来特殊模板类。例如,假设我有以下基本模板类:

enum class BasicType
{
    UNKNOWN,
    PRIMITIVE,
    OBJECT
};

template <typename T>
struct traits
{
    static constexpr BasicType type = BasicType::UNKNOWN;
};
Run Code Online (Sandbox Code Playgroud)

我希望能够创建特征的专门化,例如,对于 std::is_integral_v 为 true 的任何类型。然后是另一个专业,其中另一个特征是正确的。

我尝试过这样的事情

template <typename T>
struct traits<std::enable_if_t<std::is_integral_v<T>, T>>
{
    static constexpr BasicType type = BasicType::PRIMITIVE;
};
Run Code Online (Sandbox Code Playgroud)

但这行不通。

c++ templates template-specialization c++17

3
推荐指数
1
解决办法
156
查看次数

在 std::function 中捕获 lambda 会产生额外的副本

我正在尝试编写一些代码,通过将函数调用及其参数存储在 lambda/std::function 中,允许我稍后调用函数。理想情况下,参数只会被复制一次(并以其他方式移动),但我可以实现的最小副本数似乎是 2。

//==============================================================================
// INCLUDES
//==============================================================================

#include <iostream>
#include <functional>
#include <memory>

//==============================================================================
// VARIABLES
//==============================================================================

static std::unique_ptr<std::function<void()>> queueFunction;

//==============================================================================
// CLASSES
//==============================================================================

class Test {
public:
    Test(int a, int b = 20, int c = 30) : _a(a), _b(b), _c(c) {
        std::cout << "Test: Constructor" << std::endl;
    }
    
    ~Test() {
        std::cout << "Test: Destructor" << std::endl;
    }
    
    Test(const Test& other) :
        _a(other._a)
    {
        std::cout << "Test: Copy Constructor" << std::endl;
    }
    
    Test(Test&& other) :
        _a(std::move(other._a))
    {
        std::cout …
Run Code Online (Sandbox Code Playgroud)

c++ lambda std-function

2
推荐指数
1
解决办法
448
查看次数

优化静态字符串的字符串存储

我知道在 C/C++ 中,如果您编写一个字符串文字,它实际上会被放入具有静态(程序生命周期)存储的只读内存中。因此,例如:

void foo(const char* string) {
    std::cout << static_cast<void const*>(string) << std::endl;
}

int main() {
    foo("Hello World");
}
Run Code Online (Sandbox Code Playgroud)

应该打印出指向只读内存中某个位置的指针。

这是我的问题,假设我想编写一个写时复制 String类,它对这样的静态数据进行优化。与其将整个字符串复制到动态分配的内存中(这很昂贵),为什么不只保留指向静态数据的指针呢?然后,如果确实需要进行写入,那么我可以在此时制作一个副本。

但是我如何判断字符串是静态的还是类似的:

int main() {
    char[] myString = "Hello World";
    foo(myString);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,myString 位于堆栈中而不是堆中,因此它的生命周期不是静态的。

我的第一个想法是 的特殊构造函数std::string_view,但我不确定这std::string_view是否意味着具有静态生命周期的字符串......

c++ string

2
推荐指数
1
解决办法
69
查看次数

如何避免在易失性类中重复方法

假设我有以下非常简单的类:

class A
{
public:
    static constexpr A make() { return A{}; }

    constexpr A() : _v(0) {}

    constexpr A& setV(int v) { _v = v; return *this; }

private:
    int _v;
};
Run Code Online (Sandbox Code Playgroud)

如果我尝试按如下方式使用它:

int main()
{
    volatile A a1;
    a1.setV(10);

    //OR

    //The optimizer will optimize this chain of constexpr calls into a single "store" instruction
    a1 = A::make().setV(10); //More chaining here

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

该代码将无法编译。

我理解为什么这是真的,基于:定义易失性类对象

我知道解决方案是添加一个额外的方法,如下所示:

class A
{
public:
    constexpr A() : _v(0) {}

    volatile A& …
Run Code Online (Sandbox Code Playgroud)

c++ volatile member-functions explicit-object-parameter

2
推荐指数
1
解决办法
76
查看次数

如何为 std::shared_ptr 和 std::unique_ptr 编写模板化工厂函数

我经常编写具有类似于以下签名的工厂:

std::unique_ptr<AbstractType> createUnique(IDType id);
std::shared_ptr<AbstractType> createShared(IDType id);
Run Code Online (Sandbox Code Playgroud)

前者会做类似的事情:

switch (id)
{
case kID1:
    return std::make_unique<Type1>();
}
Run Code Online (Sandbox Code Playgroud)

而后者

switch (id)
{
case kID1:
    return std::make_shared<Type1>();
}
Run Code Online (Sandbox Code Playgroud)

那么,这两个函数之间的唯一区别是使用的“make”函数(make_sharedmake_unique)和返回类型(shared_ptrunique_ptr)。这会导致代码重复。

我想知道如何编写一个create接受指针类型和“make”函数类型并使用它们的模板化函数。就像是:

template <typename PTR, typename MAKE>
PTR create(IDType id)
{
    switch (id)
    {
    case kID1:
        return MAKE<Type1>();
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道以上不是有效的代码。

另外,我知道 astd::shared_ptr可以从 an 创建,std::unique_ptr反之亦然,但我这个工厂将用于不同的应用程序,其中一个可能使用 a shared_ptr,另一个可能使用unique_ptr. 这样,代码就可以重复使用,而且对于特定的用例也很有效。

c++ templates smart-pointers c++17

2
推荐指数
1
解决办法
108
查看次数