【问题标题】:C/C++ Macro for null safe pointer access用于空安全指针访问的 C/C++ 宏
【发布时间】:2016-06-30 12:50:52
【问题描述】:

我想编写一个用于空安全指针访问的 C/C++ 宏。我目前有这个,效果很好:

#define NULL_SAFE(p, e) if (p) p->e
NULL_SAFE(myPtr, myMethod(myArg));

但我真正想要的是拥有这样的东西:

NULL_SAFE(
  myPtr, myMethod(myArg),
  myOtherPtr, myOtherMethod(myOtherArg),
  yetAnotherMyPtr, plsStopMethod(grArg),
  ...
);

这将扩展为:

  if (myPtr) myPtr->myMethod(myArg);
  if (myOtherPtr) myOtherPtr->myOtherMethod(myOtherArg);
  if (yetAnotherMyPtr) yetAnotherMyPtr->plsStopMethod(grArg);

我能想到一大堆我可能喜欢使用的这些,但它们都在与这个相同的概念上运作。

这可能吗?这是否已经存在于某个地方?有什么建议么?感谢您的帮助!

【问题讨论】:

  • 您使用的是 C 还是 C++?如果使用 C++,您可能需要使用智能指针。
  • 宏可能看起来很棒,但它们通常会使代码难以阅读和维护。所以我的建议是不要为此使用宏,而是使用显式代码。
  • 比宏更好的是一个函数获取指针并在其上执行一些东西:nullSafe(myPtr, [&](auto& pointee) { pointee.myMethod(myArg); });。不过,此时,您基本上拥有一个 optional 和一个 map 函数。
  • 我真的不明白这么通用的东西会有什么用。通常,当您进行空值检查时,您必须以某种方式处理 空值条件。在什么情况下,您希望仅取消引用有效指针,而忽略无效指针?在这种情况下,使用 std::map 之类的容器不是更好吗?
  • 如果指针为空,您是否真的通过静默跳过语句获得了一些东西?我更喜欢漂亮的崩溃,显示程序的错误。 (一个不同的故事是一个名为 IF_NOT_NULL 的宏)

标签: c++ c pointers null macros


【解决方案1】:

如果 NULL 检查是算法的一部分,那么只需明确键入 NULL 检查,不要使用任何 icky 宏。

如果 NULL 检查是一种防御性编程方式,那么正确的做法是assert(ptr);。如果断言曾经触发,请修复导致它的错误。重复直到没有留下任何错误,然后从生产质量代码中删除断言。

【讨论】:

  • 除非空指针是程序的可接受状态。请参阅我对 CodyGray 评论的回复。感谢您的建议!
【解决方案2】:

C++11:

inline void null_safe()
{
}

template <typename Ptr, typename Fn, typename... Args>
void null_safe(Ptr&& ptr, Fn&& fn, Args&&... args)
{
    if (ptr)
        fn();
    // you could put "else" here                                                                                                            
    null_safe(std::forward<Args>(args)...);
}

您可以使用任何可调用对象作为第二个参数,因此:

int f2() {
    return printf("f2\n");
}

int f3() {
    return printf("f3\n");
}

int main()
{
    int i1 = 1;

    null_safe(
        &i1, f2
        );

    null_safe(
        NULL, f2,
        &i1, f3
        );
}

您也可以使用任何谓词作为第一个参数。

为什么这里是 NULL 而不是 nullptr 留给读者作为练习。

【讨论】:

  • 我不得不用谷歌搜索那个。 nullptr 评估为指针类型,而 NULL 评估为整数,这显然是您的模板工作所必需的。感谢您的练习!
  • 如果走模板之路,您可能会将参数传递给函数...
【解决方案3】:

我能做到的最接近的方法是使用 c++11 可变参数模板和 C99 可变参数宏...抱歉,如果您的平台不允许这样做,尽管编写代码很有趣!

#include <functional>
#include <iostream>

template<class T>
void stuff(T a)
{
    std::cout<< "stuff:" << a << std::endl;
}

template<class T>
void other_stuff(T a)
{
    std::cout<< "other_stuff:" << a << std::endl;
}

template <typename Test, typename ToCall>
void tester(Test t, ToCall tc)
{
    if(t) tc();
}

template <typename Test, typename ToCall, typename... Others>
void tester(Test t, ToCall tc, Others... args)
{
    if(t) tc();
    tester(args...);
}

#define FUN_WRAP(a,b) std::bind(a<decltype(b)>, (b) )
#define NULL_SAFE(...) tester(__VA_ARGS__)

int main()
{
    NULL_SAFE(1, FUN_WRAP(stuff, 1),
        0, FUN_WRAP(stuff, 2),
        1, FUN_WRAP(other_stuff, 3)
        );
}

【讨论】:

  • 这看起来很棒,但是一旦你有了可变参数模板函数,你还需要 NULL_SAFE 宏吗?我也在寻找一个宏,所以它可以移植到 pre c++11 编译器和 C 中,但如果事实证明纯宏解决方案是不可能的,那就太好了!
  • @RoboCop87:纯宏观解决方案是可能的。但是认真思考太恶心了。
  • 不,你真的不需要NULL_SAFE 宏。
  • @JohnZwinck “太恶心了,无法认真思考”让我更想学习如何去做,如果不仅仅是为了学术练习!
猜你喜欢
  • 2013-06-10
  • 2015-10-18
  • 2018-07-29
  • 2014-05-13
  • 2015-04-13
  • 1970-01-01
  • 2020-02-13
  • 2013-08-12
  • 1970-01-01
相关资源
最近更新 更多