C/C++宏编程

本文介绍了C/C++宏编程在实际项目中的应用,包括通信协议实现、回调函数处理和回调封送等示例,展示了通过宏如何减少代码重复和提高代码效率。尽管宏的使用可能带来潜在风险,但在特定场景下,如调试宏、ANSI/Unicode转换等,宏仍然是不可或缺的工具。作者讨论了宏与模板、多态等技术的比较,并强调了宏的灵活性和维护性优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C/C++宏编程

宏的复杂使用,永远不要写两次!

介绍

我读过的所有C/C++教科书都批评宏的使用。“不要使用它们,它们很危险,因为它们隐藏了你实际写的东西。尤其是看起来很实用的宏。有些人甚至说,没有理由在C++的模板类的发明中使用宏。

尽管如此,宏仍然在某些地方使用。

例如,调试宏,如 、 等。它们都是看起来函数的宏,在调试和发布版本下扩展到不同的东西。ASSERTVERIFYTRACE

我知道有些人从不使用这些宏。相反,它们只是使用在调试和发布版本中以不同方式实现的函数。为什么?因为他们害怕使用看起来很函数的宏的威胁。也就是说,他们希望始终计算内部表达式,即使在不使用其值的发布版本中也是如此。好吧,这对某些人来说可能是合理的。事实上,将一些重要的代码放在里面(而不是)是一个非常常见的错误。ASSERTASSERTVERIFY

我个人总是使用这些的宏版本。那是因为我非常频繁地使用调试宏来在出现问题时立即获得指示,而另一方面,我不希望最终的可执行文件附带所有这些废话。

另一个广泛使用的宏的例子与ANSI/Unicode有关。这包括 ()、所有类似宏的宏。此外,Windows API 具有许多带有 或 后缀的函数,例如 、.也就是说,实际上是一个宏,它根据构建设置扩展到其中一个宏。_T_tprintfAWGetMessageAGetMessageWGetMessage

尽管受到批评,但宏仍在使用。有人可能会争论这是否合理,但这不是本文的主题。我想在本文中展示通过复杂使用宏可以完成的非常令人惊奇的事情。是否使用这些技术 - 决定取决于您。

通信协议示例

假设您必须实现一些通信协议。该协议由不同类型的“消息”组成,每条消息都有其特定的参数。

让我们同意(现在)每个传输的消息都从其 4 字节大小开始(从而将最大的消息限制为 4GB 的数量级),然后是它的 2 字节代码,然后是它的所有参数,这些参数是消息相关的。序号类型 (、、、、...) 按原样传输(不进行大端/小端转换)。字符串以 Unicode 字符集 (UTF16) 传输,前面的字符以字符为单位指定字符串的长度。ULONGUCHARUSHORTdoubleULONG

首先,我们需要以下消息类型:

  1. 登录。包含客户端版本 ()、用户名(字符串)、密码(字符串)。ULONG
  2. 登录结果。包含登录结果代码 ()。0=正常,1=凭据无效,依此类推。UCHAR
  3. 聊天消息。包含收件人用户名(字符串)、聊天文本(字符串)、一些额外的代码 ()。ULONG

那么,我们如何实现这一点呢?对于每种消息类型,我们需要以下内容:

  • 消息类/结构的声明。
  • 将此消息写入流(套接字/文件/等)的代码。
  • 分析流中的消息的代码。
  • 处理传入消息的代码。

出于此示例的目的,我们将使用以下抽象类进行流式处理:

C++

收缩 ▲   

struct OutStream {

    virtual void Write(LPCVOID pPtr, size_t nSize) = 0;

    // ordinal types    template <class T>

    void Write_T(const T& val)

    {

        Write(&val, sizeof(val));

    }

    // variable-sized strings    void Write_Str(const CString& val)

    {

        ULONG nLen = val.GetLength();

        Write_T(nLen);

        Write((PCWSTR) val, nLen * sizeof(WCHAR));

    }

};struct InStream {

    virtual size_t Read(LPVOID pPtr, size_t nSize) = 0;

    bool ReadExactTry(LPVOID pPtr, size_t nSize)

    {

        while (true)

        {

            size_t nPortion = Read(pPtr, nSize);

            if (nPortion == nSize)

                return true; // ok            if (!nPortion)

                return false; // not enough data.            nSize -= nPortion;

            if (pPtr)

                ((PBYTE&) pPtr) += nPortion;

        }

    }

    void ReadExact(LPVOID pPtr, size_t nSize)

    {

        if (!ReadExactTry(pPtr, nSize))

        {

            // not enough data, raise an appropriate exception            throw _T("not enough data!");

        }

    }

    // ordinal types    template <class T>

    void ReadExact_T(T& val)

    {

        ReadExact(&val, sizeof(val));

    }

    // variable-sized strings    void ReadExact_Str(CString& val)

    {

        ULONG nLen;

        ReadExact_T(nLen);

        PWSTR szPtr = val.GetBuffer(nLen);

        ReadExact(szPtr, nLen * sizeof(WCHAR));

        val.ReleaseBuffer(nLen);

    }

};

现在,让我们实现我们的消息。例如,可以按以下方式声明登录消息:

C++

收缩 ▲   

struct MsgLogin

{

    // message fields    ULONG m_Version;

    CString m_Username;

    CString m_Password;

    MsgLogin()

    {

        // zero-init members.        m_Version =

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值