在代码中间使用#include是不好的

Is it bad to use #include in the middle of code?

本文关键字:#include 代码 中间      更新时间:2023-10-16

我一直在读到这样做是不好的,但是我没有完全回答我的特定问题。

在某些情况下,它似乎真的很有用。我想做以下操作:

class Example {
    private:
        int val;
    public:
        void my_function() {
#if defined (__AVX2__)
    #include <function_internal_code_using_avx2.h>
#else
    #include <function_internal_code_without_avx2.h>
#endif
        }
};

如果在代码中间使用#include在此示例中很糟糕,那么实现我要做的事情将是一种好的实践方法?也就是说,我试图在AVX2且无法编译的情况下区分成员函数实现。

no no本质上并不糟糕。#include的目的是允许在任何地方包括。只是这样使用它并不常见,这违反了至少惊讶的原则。

周围开发的良好实践包括所有的良好实践都是基于在汇编单元开始时以及在任何名称空间之外的原则上纳入的假设。

这无疑是为什么C 核心指南建议不要这样做的原因,因此被理解为它们具有正常的可重复使用的标题:

sf.4:在文件中的其他声明

中添加.h文件

原因

最小化上下文依赖性并提高可读性。

其他备注:如何解决基本问题

不确定完整的上下文。但是首先,我不会将功能体系放在类定义中。这将更好地囊括了班级消费者的实施特定细节,不需要知道。

然后,您可以在身体中使用条件汇编,或者更好地选择一些基于策略的设计,使用模板配置要在编译时间使用的类">

我同意@Christophe所说的话。在您的情况下,我会编写以下代码

写一个标题commonInterface.h

#pragma once
#if defined (__AVX2__)
    void commonInterface (...) {
        #include <function_internal_code_using_avx2.h>
    }
#else
    void commonInterface (...) {
        #include <function_internal_code_without_avx2.h>
    }
#endif

因此,您将#if defined隐藏在标题中,并且在实现文件中仍然具有良好的可读代码。

#include <commonInterface>
class Example {
    private:
        int val;
    public:
        void my_function() {
            commonInterface(...);
        }
};
#ifdef __AVX2__
#   include <my_function_avx2.h>
#else
#   include <my_function.h>
#endif
class Example {
    int val;
public:
    void my_function() {
#       ifdef __AVX2__
        my_function_avx2(this);
#       else
        my_function(this);
#       endif
    }
};

它是好还是坏,实际上取决于上下文。如果您必须编写大量的样板代码,通常会使用该技术。例如,Clang编译器在整个地方都使用它来匹配/使用所有可能的类型,标识符,令牌等。这是一个例子,这里是另一个。

如果您想根据某些编译时已知参数来定义一个函数,则可以将其定义放置在属于位置的情况下。您不应将foo的定义分为两个单独的文件,并在编译时选择正确的文件,因为它增加了程序员的开销(通常不仅仅是您)来理解您的代码。考虑以下片段,至少在我看来,它更具表现力:

// platform.hpp 
constexpr static bool use_avx2 = #if defined (__AVX2__)
                               true;
                             #else
                               false;
                             #endif
// example.hpp
class Example {
private:
    int val;
public:
    void my_function() {
        if constexpr(use_avx2) {
            // code of "functional_internal_code_using_avx2.h"
        }
        else {
            // code of "functional_internal_code_without_avx2.h"
        }
};

可以通过概括更多,添加"仅定义算法"的抽象层,而不是算法和平台特定的怪异。

,可以进一步改进代码。

反对您解决方案的另一个重要论点是functional_internal_code_using_avx2.hfunctional_internal_code_without_avx2.h都需要特别注意:它们不会没有example.h构建,如果没有打开所需的任何文件,这并不明显。因此,必须添加构建项目时的特定标志/处理,一旦您使用多个这样的 functional_internal_code -files。p>我不确定您在您的情况下是什么,所以无论以下什么都应该用一粒盐来服用。

无论如何:#include可能发生在代码中的任何地方,但是您可以将其视为将代码/避免冗余的一种方式。对于定义,这已经被其他方式很好地涵盖了。对于声明,这是标准方法。

现在,此#include S开始放置在读者的礼节上,他们可以更快地赶上代码中的期望,即使对于#ifdef守卫代码。

在您的情况下,您似乎想要相同功能的不同实现。在这种情况下,待办事项的方法是 link 另一部分代码(包含不同的实现),而不是导入不同的声明。

相反,如果您想根据您的#ifdef真正具有不同的签名,那么我看不到比在代码中间有#ifdef更有效的方法。但是,我不会认为这是一个不错的设计选择!

我将其定义为对我的不良编码。它使代码难以读取。

我的方法是将基类创建为抽象接口并创建专业实现,然后创建所需的类。

例如:

class base_functions_t
{
public:
    virtual void function1() = 0;
}
class base_functions_avx2_t : public base_functions_t
{
public:
    virtual void function1()
    {
        // code here
    } 
}
class base_functions_sse2_t : public base_functions_t
{
public:
    virtual void function1()
    {
        // code here
    }
}

然后,您可以将指针指向base_functions_t并进行不同版本。例如:

base_functions_t *func;
if (avx2)
{
    func = new base_functions_avx2_t();
}
else
{
    func = new base_functions_sse2_t();
}
func->function1();

作为一般规则,我会说最好将定义接口的标题放在您的实现文件中。

当然还有标头不会定义任何接口。我主要考虑使用宏观黑客的标题,并打算包括一次或多次。这种类型的标头通常没有包括后卫。一个示例是<cassert>。这使您可以编写类似的代码

#define NDEBUG 1
#include <cassert>
void foo() {
    // do some stuff
    assert(some_condition);
}
#undef NDEBUG
#include <cassert>
void bar() {
    assert(another_condition);
}

如果您在文件开始时仅包含<cassert>,则除了全部或全部关闭以外,您将没有任何粒度的主张。有关此技术的更多讨论,请参见此处。

如果您确实按照您的示例使用条件包含的路径,那么我强烈建议您使用Eclipse或Netbeans之类的编辑器,这些编辑器可以进行内联预处理器的扩展和可视化。否则,包容带来的地方丧失可能会严重损害可读性。