在代码中间使用#include是否很糟糕?

时间:2019-02-22 07:37:07

标签: c++

我一直在读,这样做很不好,但是我觉得这些答案并不能完全回答我的特定问题。

在某些情况下似乎很有用。我想做以下事情:

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是否可以编译的情况下,我试图区分成员函数的实现。

7 个答案:

答案 0 :(得分:6)

不,它从本质上来说并不坏。 #include旨在允许包含任何位置。只是这样使用它并不常见,这与principle of least astonishment背道而驰。

围绕包含开发的良好实践都是基于在编译单元开始时包含一个包含对象的假设,并且原则上在任何命名空间之外。

这当然是C++ core guidelines建议不要这样做的原因,因为他们了解到它们具有正常的可重复使用标头:

  

SF.4:在文件中的其他声明之前包含.h文件

     

原因

     

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

附加说明:如何解决基本问题

不确定完整的上下文。但是首先,我不会将函数体放入类定义中。这将更好地为类使用者封装实现特定的细节,而这无需了解。

然后,您可以在模板中使用条件编译,或者更好地选择使用policy based design,使用模板来配置要在编译时使用的类。

答案 1 :(得分:1)

我同意@克里斯托夫的话。您的情况下,我将编写以下代码

写标题sqoop import --connect 'jdbc:sqlserver://10.0.2.11:1433;database=SP2010' --username pbddms -P --table daily_language --hive-import -m 1 --hive-database test_hive --hive-table daily_language --hive-overwrite --hive-drop-import-delims --null-string '\\N' --null-non-string '\\N'

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

答案 2 :(得分:1)

from django.conf import settings

from channels.generic.websocket import AsyncJsonWebsocketConsumer

from .exceptions import ClientError
from .utils import get_room_or_error


class ChatConsumer(AsyncJsonWebsocketConsumer):
    """
    This chat consumer handles websocket connections for chat clients.

    It uses AsyncJsonWebsocketConsumer, which means all the handling functions
    must be async functions, and any sync work (like ORM access) has to be
    behind database_sync_to_async or sync_to_async. For more, read
    http://channels.readthedocs.io/en/latest/topics/consumers.html
    """

    ##### WebSocket event handlers

    async def connect(self):
        """
        Called when the websocket is handshaking as part of initial connection.
        """
        # Are they logged in?
        if self.scope["user"].is_anonymous:
            # Reject the connection
            await self.close()
        else:
            # Accept the connection
            await self.accept()
        # Store which rooms the user has joined on this connection
        self.rooms = set()

    async def receive_json(self, content):
        """
        Called when we get a text frame. Channels will JSON-decode the payload
        for us and pass it as the first argument.
        """
        # Messages will have a "command" key we can switch on
        command = content.get("command", None)
        try:
            if command == "join":
                # Make them join the room
                await self.join_room(content["room"])
            elif command == "leave":
                # Leave the room
                await self.leave_room(content["room"])
            elif command == "send":
                await self.send_room(content["room"], content["message"])
        except ClientError as e:
            # Catch any errors and send it back
            await self.send_json({"error": e.code})

    async def disconnect(self, code):
        """
        Called when the WebSocket closes for any reason.
        """
        # Leave all the rooms we are still in
        for room_id in list(self.rooms):
            try:
                await self.leave_room(room_id)
            except ClientError:
                pass

    ##### Command helper methods called by receive_json

    async def join_room(self, room_id):
        """
        Called by receive_json when someone sent a join command.
        """
        # The logged-in user is in our scope thanks to the authentication ASGI middleware
        room = await get_room_or_error(room_id, self.scope["user"])
        # Send a join message if it's turned on
        if settings.NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS:
            await self.channel_layer.group_send(
                room.group_name,
                {
                    "type": "chat.join",
                    "room_id": room_id,
                    "username": self.scope["user"].username,
                }
            )
        # Store that we're in the room
        self.rooms.add(room_id)
        # Add them to the group so they get room messages
        await self.channel_layer.group_add(
            room.group_name,
            self.channel_name,
        )
        # Instruct their client to finish opening the room
        await self.send_json({
            "join": str(room.id),
            "title": room.title,
        })

    async def leave_room(self, room_id):
        """
        Called by receive_json when someone sent a leave command.
        """
        # The logged-in user is in our scope thanks to the authentication ASGI middleware
        room = await get_room_or_error(room_id, self.scope["user"])
        # Send a leave message if it's turned on
        if settings.NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS:
            await self.channel_layer.group_send(
                room.group_name,
                {
                    "type": "chat.leave",
                    "room_id": room_id,
                    "username": self.scope["user"].username,
                }
            )
        # Remove that we're in the room
        self.rooms.discard(room_id)
        # Remove them from the group so they no longer get room messages
        await self.channel_layer.group_discard(
            room.group_name,
            self.channel_name,
        )
        # Instruct their client to finish closing the room
        await self.send_json({
            "leave": str(room.id),
        })

    async def send_room(self, room_id, message):
        """
        Called by receive_json when someone sends a message to a room.
        """
        # Check they are in this room
        if room_id not in self.rooms:
            raise ClientError("ROOM_ACCESS_DENIED")
        # Get the room and send to the group about it
        room = await get_room_or_error(room_id, self.scope["user"])
        await self.channel_layer.group_send(
            room.group_name,
            {
                "type": "chat.message",
                "room_id": room_id,
                "username": self.scope["user"].username,
                "message": message,
            }
        )

    ##### Handlers for messages sent over the channel layer

    # These helper methods are named by the types we send - so chat.join becomes chat_join
    async def chat_join(self, event):
        """
        Called when someone has joined our chat.
        """
        # Send a message down to the client
        await self.send_json(
            {
                "msg_type": settings.MSG_TYPE_ENTER,
                "room": event["room_id"],
                "username": event["username"],
            },
        )

    async def chat_leave(self, event):
        """
        Called when someone has left our chat.
        """
        # Send a message down to the client
        await self.send_json(
            {
                "msg_type": settings.MSG_TYPE_LEAVE,
                "room": event["room_id"],
                "username": event["username"],
            },
        )

    async def chat_message(self, event):
        """
        Called when someone has messaged our chat.
        """
        # Send a message down to the client
        await self.send_json(
            {
                "msg_type": settings.MSG_TYPE_MESSAGE,
                "room": event["room_id"],
                "username": event["username"],
                "message": event["message"],
            },
        )

答案 3 :(得分:1)

它的好坏实际上取决于上下文。 如果必须编写大量样板代码,通常会使用该技术。例如,clang编译器在各处都使用它来匹配/利用所有可能的类型,标识符,令牌等。 Here是一个示例,here是另一个示例。

如果要根据某些编译时已知参数来不同地定义函数,可以将定义放在它们所属的位置,这看起来更干净。 您不应将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这样的文件,就很难维护。

答案 4 :(得分:1)

我不确定您的情况如何,因此应随心所欲地采取任何措施。

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

现在,#include的开头是出于对读者的礼貌,以使读者可以更快地掌握代码中的预期内容,即使是受#ifdef保护的代码也是如此。< / p>

在您的情况下,您似乎想要相同功能的不同实现。在这种情况下,要采用的方法是链接代码的不同部分(包含不同的实现),而不是导入不同的声明。

相反,如果您确实想根据自己的#ifdef使用不同的签名,那么我将没有比在代码中间使用#ifdef更有效的方法了。但是,我认为这不是一个好的设计选择!

答案 5 :(得分:0)

我将此定义为对我而言不好的编码。它使代码难以阅读。

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

例如:

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();

答案 6 :(得分:0)

一般来说,最好将定义接口的标头放在实现文件中。

当然也有没有定义任何接口的标头。我主要考虑的是使用宏黑客的标头,并且打算被包含一次或多次。这种类型的标题通常没有包含防护。一个示例为<cassert>。这样您就可以编写类似这样的代码

#define NDEBUG 1
#include <cassert>

void foo() {
    // do some stuff
    assert(some_condition);
}

#undef NDEBUG
#include <cassert>

void bar() {
    assert(another_condition);
}

如果仅在文件的开头包含<cassert>,则除了全部打开或全部关闭之外,您将没有粒度实现文件中的断言。有关此技术的更多讨论,请参见here

如果确实按照示例使用条件包含,那么我强烈建议您使用像Eclipse或Netbeans这样的编辑器,它们可以进行内联预处理器扩展和可视化。否则,包含所带来的局部性损失会严重损害可读性。