标签: variadic

如何帮助程序员在C中编写安全正确的printf调用?

[为清晰起见,更新了组织和内容]

真正的问题

对于C来说,帮助程序员在他/她打字的同时,对项目特定的类似printf的调试函数进行安全和正确的调用会有什么好方法?

C宏?C包装函数?代码编辑器宏或模板?其他?

背景问题和答案

许多软件使用printf或类似printf的函数进行调试,无论是出现问题时是临时还是调试日志.但它容易出错.

Q1:我们怎么知道?
A1:静态分析器有printf-mismatch错误的类别 - 这是一类常见的错误 - 我经常看到这些工具在C代码上调出这些警告.

Q2:这个错误的子类是什么?
A2:主要是格式说明符错误,格式说明符号错误.通常真正的错误是相反的:错误的变量类型,或打印输出的错误数量的变量.

Q3:我们为什么关心?
A3:充其量,会导致错误的日志记录信息并阻碍调试.最糟糕的是,崩溃软件.

问题4:有没有人试图对此问题采取任何措施?
A4:当然,虽然我没有看到任何C(而不是C++或其他),例如:

http://www.ddj.com/cpp/184401999?pgno=1 http://mi.eng.cam.ac.uk/~er258/cvd/tag/html/group__printf.html

在这些产品和其他产品中我缺少的是什么,除了现在我正在研究用C语言编写并且需要解决C问题的产品,这是因为它们是事后解决方案.他们能避免碰撞,并能提供的什么地方出了错警示说明,并出事了,但他们肯定无法猜测程序员的意图(见电除尘器.Q&上述A#2).

Q5:为什么使用printf这么容易出错?
A5:因为编写printf调用需要程序员在一行上混合变量的类型和数量,格式说明符,自由文本字符串常量和标点符号 - 所有这些看起来非常相似.

c printf variadic

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

关于可变参数模板

我目前正在使用新的c ++ 0x可变参数模板,这很有趣,虽然我对成员实例化的过程有疑问.

在这个例子中,我试图模拟强类型枚举,可以选择随机有效的强枚举(这用于单元测试).


#include<vector>
#include<iostream>

using namespace std;

template<unsigned... values> struct sp_enum;

/*
 this is the solution I found, declaring a globar var
 vector<unsigned> _data;

 and it work just fine

*/

template<> struct sp_enum<>{
  static const unsigned _count  = 0;
  static vector<unsigned> _data;
};

vector<unsigned> sp_enum<>::_data;

template<unsigned T, unsigned... values>
struct sp_enum<T, values...> : private sp_enum<values...>{
  static const unsigned _count = sp_enum<values...>::_count+1;
  static vector<unsigned> _data;

  sp_enum(                       ) : sp_enum<values...>(values...) {_data.push_back(T);}
  sp_enum(unsigned v             )                                 {_data.push_back(v);}
  sp_enum(unsigned v, unsigned...) : sp_enum<values...>(values...) …
Run Code Online (Sandbox Code Playgroud)

c++ templates metaprogramming variadic c++11

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

C ++变量参数

我有一个带有状态机的类,并且希望有一个入口点将事件传递给状态机。该事件伴随有事件特定的数据,然后我希望将其分发给处理程序。所以看起来像这样...

class X
{
  public:
    ...
    template<typename... A> void fsm(eEvent eventId, const A&... eventData);

  private:
    ...
    void eventA(int a, double b);
    void eventB(std::string a);
    void eventC(unsigned long a);
Run Code Online (Sandbox Code Playgroud)

};

...看起来像这样的调用...

X x;

x.fsm(eEventA, -1, 2.0);
x.fsm(eEventB, "xyz");
x.fsm(eEventC, 42);
Run Code Online (Sandbox Code Playgroud)

我在弄清楚如何获取模板函数来调用正确的处理程序时遇到了麻烦。如果我只是打开eventId并通过变量参数,它将不会编译,因为对于所有参数组合都不存在处理程序(例如,没有eventA()处理程序接受eventB()参数,我不会仍然要)。

我的猜测是有一些优雅的方法可以做到这一点,但这让我难以理解。

c++ templates function variadic

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

如何推断变量参数后的模板参数?

我有以下模板功能:

template <typename...Args, typename Func>
void call(const char *name, Args...args, Func f)
{
        f(3);
}
Run Code Online (Sandbox Code Playgroud)

当我尝试使用它时,就像

    call("test", 1, 2, 3, [=](int i) { std::cout<< i; });
Run Code Online (Sandbox Code Playgroud)

编译器抱怨它无法推断模板参数Func.如何解决这个问题,知道args除了函数指针之外的任何类型.

c++ templates variadic c++11

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

C++:从可变参数模板创建自定义函数调度程序

我有一些从序列化数据中读取各种类型的函数,例如:

class DataDeserializer
{
    int getInt();
    std::string getString();
    MyClass getMyClass();
}
Run Code Online (Sandbox Code Playgroud)

然后我有各种带有任意参数的回调函数,例如:

void callbackA (int, int, int);
void callbackB (int, std::string);
void callbackC (std::string, int, MyClass, int);
Run Code Online (Sandbox Code Playgroud)

我想调用从反序列化数据流中读取的参数的各种回调.我想要的是尽可能自动化样板代码.我想也许我可以使用模板.如果我有某种Dispatcher类,例如:

template <SOMETHING??> class Dispatcher
{
    void dispatch()
    {
        // ???? 
    }

    SOMEFUNCTIONTYPE callback;
    DataDeserializer myDeserializer;
};
Run Code Online (Sandbox Code Playgroud)

然后声明各种特定的调度员:

Dispatcher<int,int,int>                  myDispatcherA (deserializer, callbackA);
Dispatcher<int,std::string>              myDispatcherB (deserializer, callbackB);
Dispatcher<std::string,int,MyClass,int>  myDispatcherC (deserializer, callbackC);
Run Code Online (Sandbox Code Playgroud)

然后,当我想发送时,我只是打电话给:

myDispatcherB.dispatch();
Run Code Online (Sandbox Code Playgroud)

下面会扩展到这样的东西:

void dispatch()
{
    callback (myDeserializer.getString(), myDeserializer.getInt(), myDeserializer.getMyClass(), myDeserializer.getInt());
}
Run Code Online (Sandbox Code Playgroud)

这可能与C++ 11可变参数模板有关吗?我已经阅读了一些关于它们的内容,似乎递归使用了很多.

c++ templates variadic

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

可变基类的初始化

以下代码不起作用.它的目的是将参数传递给可变参数基类.这是可能的,如果是这样,实施它的正确方法是什么?(Clang的错误信息是:an initializer for a delegating constructor must appear alone,我不明白.)

template<class... Visitors>
class Visited: public Visitors...
{
    public:
        template<class F>
        Visited(const F& f): F(f)               {}

        template<class F, class... Rest>
        Visited(const F& f, const Rest&... r):
            F(f), Visited(r...)                       {}
};

struct A {};
struct B {};
struct C {};

int main()
{
    A a;
    B b;
    C c;
    Visited<A,B,C> v(a, b, c);
}
Run Code Online (Sandbox Code Playgroud)

c++ templates variadic c++11

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

具有递归可变函数的stringstream?

我希望能够使用ostringstream将多个不同的参数组合成一个字符串.这样我就可以记录生成的单个字符串而不会出现任何随机问题.

我到目前为止:

template <typename T>
    void MagicLog(T t)
    {
        std::cout << t << std::endl;
    }

    template<typename T, typename... Args>
    void MagicLog(T t, Args... args) // recursive variadic function
    {
        std::cout << t << std::endl;

        MagicLog(args...);
    }

    template<typename T, typename... Args>
    void LogAll(int logType, T t, Args... args)
    {
        std::ostringstream oss;
        MagicLog(t);
        MagicLog(args...);
        //Log(logType, oss.str());
    }
Run Code Online (Sandbox Code Playgroud)

所以我需要用我在LogAll函数中创建的oss替换std :: cout,我尝试将它作为参数传递给其他函数,但它抱怨"删除函数"...

那么:我怎样才能获得一个递归的可变参数函数来接受另一个参数ostringstream?

c++ recursion variadic ostringstream

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

使用可变参数模板和lambda函数进行二进制搜索

想想这个,

struct Person {
    std::string name;
    Person (const std::string& n) : name(n) {}
    std::string getName(int, char) const {return name;}  // int, char play no role in this
        // simple example, but let's suppose that they are needed.
} *Bob = new Person("Bob"), *Frank = new Person("Frank"), *Mark = new Person("Mark"),
    *Tom = new Person("Tom"), *Zack = new Person("Zack");

const std::vector<Person*> people = {Bob, Frank, Mark, Tom, Zack};
Run Code Online (Sandbox Code Playgroud)

因为people按名称排序,我们可以执行二进制搜索以查找people具有特定名称的元素.我希望这个看起来像是这样的

Person* person = binarySearch (people, "Tom",
   [](Person* p, …
Run Code Online (Sandbox Code Playgroud)

c++ binary templates variadic c++11

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

Ltac:可选参数策略

我想在coq中制作一个Ltac战术,它可以采用1或3个参数.我ltac_No_argLibTactics模块中已经阅读过,但如果我理解正确,我将不得不调用我的策略:

Coq < mytactic arg_1 ltac_no_arg ltac_no_arg.
Run Code Online (Sandbox Code Playgroud)

这不是很方便.

有没有办法得到这样的结果?:

Coq < mytactic arg_1.

Coq < mytactic arg_1 arg_2 arg_3.
Run Code Online (Sandbox Code Playgroud)

variadic coq ltac

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

如何将切片的每个元素作为单独的参数传递给可变参数C函数?

我正在Rust中构建一个Redis模块。我找到了一些很好的例子,但是在处理与应该接受可变参数的C函数接口时,我陷入了困境。

Redis Module C SDK具有一个名为的函数RedisModule_Call,该函数接受一些特定的参数,然后接受n代表Redis命令的参数。从Redis Module SDK文档(在C中):

RedisModuleCallReply *reply;
reply = RedisModule_Call(ctx,"INCR","sc",argv[1],"10");
Run Code Online (Sandbox Code Playgroud)

RedisModule_Call的前三个参数是特定的,但其余的表示Redis命令,可以轻松地包含数百个参数。

在Rust中,我遵循Redis-Cell中的模式,Redis-Cell是在Rust中成功实现的Redis模块。该模块很棒,但是处理此特定问题的方式非常有限。实际上,它以某种蛮力的方式接受了多达三个参数:

pub fn call(&self, command: &str, args: &[&str]) -> Result<Reply, CellError> {
   // ... code ... 
   let raw_reply = match args.len() {
        1 => raw::call1::call(/* ... */),
        2 => raw::call2::call(/* ... */),
        // ...
Run Code Online (Sandbox Code Playgroud)

这些call1call2函数实际上只是处理不同参数长度的存根:

pub mod call2 {
    use redis::raw;

    pub fn call(
        ctx: *mut raw::RedisModuleCtx,
        cmdname: *const u8,
        fmt: *const …
Run Code Online (Sandbox Code Playgroud)

variadic rust

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

标签 统计

variadic ×10

c++ ×7

templates ×6

c++11 ×4

binary ×1

c ×1

coq ×1

function ×1

ltac ×1

metaprogramming ×1

ostringstream ×1

printf ×1

recursion ×1

rust ×1