用字符串文字

时间:2015-09-09 16:20:37

标签: c++ arrays

Ghostscript interpreter API有一个功能

GSDLLEXPORT int GSDLLAPI gsapi_init_with_args(void *instance, int argc, char **argv)

最后一个参数argv是一个指向C字符串数组的指针,它被解释为命令行参数。我显然无法更改函数gsapi_init_with_args的签名来取代const char **参数。

如果我愿意忽略(或沉默)deprecated conversion from string constant to 'char*'警告,那么我会简单地写一下

char *gs_argv[] = {"", "-dNOPAUSE", "-dBATCH", ...};

并传递gs_argv作为最终参数。但我更愿意修改我的代码,以便我不依赖外部函数来按照我期望的方式行事(并有效地将gs_argv视为const char**)。

有没有简单的方法将gs_argv声明为指向(非const)C字符串的指针数组,并使用字符串文字初始化其元素? (也就是说,使用类似的方法来初始化单个C字符串:使用char c_str[] = "abc"。)我能想到的最好的就是使用

const char *gs_argv0[] = {"", "-dNOPAUSE", "-dBATCH", ...};

然后将元素逐个元素复制到gs_argv

请注意,我理解为什么编译器会发出此警告(并且已经阅读了this question)的答案。我要求解决方案,而不是解释。

6 个答案:

答案 0 :(得分:2)

您可以使用:

char arg1[] = "";
char arg2[] = "-dNOPAUSE";
char arg3[] = "-dBATCH";

char* gs_argv0[] = {arg1, arg2, arg3, NULL};
int argc = sizeof(gs_argv0)/sizeof(gs_argv0[0]) - 1;
gsapi_init_with_args(instance, argc, gs_argv0)

答案 1 :(得分:1)

使用strdup创建字符串文字的副本。这更详细,但修复了警告。

char* gs_argv0[NARGS];
gs_argv0[0] = strdup("");
gs_argv0[1] = strdup("-dNOPAUSE");
// ...

请注意,如果您想防止泄密,还需要释放strdup分配的内存。

您可能还想在代码中添加评论,说明您为什么要这样做,以便为将来的读者清楚说明。

答案 2 :(得分:1)

如果你能保证该函数不会修改非const参数,那么在这种情况下可以使用const_cast

答案 3 :(得分:1)

由于此代码需要C ++ 11,因此在下面的另一个答案中有一个成本较低的C ++ 11解决方案。我将这个留给后人。

有两种选择:忽略它和const_cast,或做正确的事情。由于这是现代的C ++,你应该有很好的RAII课程。因此,最简单,最安全的做法是安全地包装这样的数组。

// https://github.com/KubaO/stackoverflown/tree/master/questions/args-cstrings-32484688
#include <initializer_list>
#include <type_traits>
#include <cstdlib>
#include <cassert>
#include <vector>

class Args {
   struct str_vector : std::vector<char*> {
      ~str_vector() { for (auto str : *this) free(str); }
   } m_data;
   void append_copy(const char * s) {
      assert(s);
      auto copy = strdup(s);
      if (copy) m_data.push_back(copy); else throw std::bad_alloc();
   }
public:
   Args(std::initializer_list<const char*> l) {
      for (auto str : l) append_copy(str);
      m_data.push_back(nullptr);
   }
   template <std::size_t N>
   Args(const char * const (&l)[N]) {
      for (auto str : l) append_copy(str);
      m_data.push_back(nullptr);
   }
   /// Initializes the arguments with a null-terminated array of strings.
   template<class C, typename = typename std::enable_if<std::is_same<C, char const**>::value>::type>
   Args(C l) {
      while (*l) append_copy(*l++);
      m_data.push_back(nullptr);
   }
   /// Initializes the arguments with an array of strings with given number of elements.
   Args(const char ** l, size_t count) {
      while (count--) append_copy(*l++);
      m_data.push_back(nullptr);
   }
   Args(Args && o) = default;
   Args(const Args &) = delete;
   size_t size() const { return m_data.size() - 1; }
   char ** data() { return m_data.data(); }
   bool operator==(const Args & o) const {
      if (size() != o.size()) return false;
      for (size_t i = 0; i < size(); ++i)
         if (strcmp(m_data[i], o.m_data[i]) != 0) return false;
      return true;
   }
};

让我们看看它是如何运作的:

#include <iostream>

extern "C" int gsapi_init_with_args(void*, int argc, char** argv) {
   for (int i = 0; i < argc; ++i)
      std::cout << "arg " << i << "=" << argv[i] << std::endl;
   return 0;
}

int main()
{
   Args args1 { "foo", "bar", "baz" };
   const char * args2i[] { "foo", "bar", "baz", nullptr };
   Args args2 { (const char **)args2i };
   const char * args3i[] { "foo", "bar", "baz" };
   Args args3 { args3i };
   const char * const args4i[] { "foo", "bar", "baz" };
   Args args4 { args4i };
   const char * args5i[] { "foo", "bar", "baz" };
   Args args5 { args5i, sizeof(args5i)/sizeof(args5i[0]) };

   assert(args1 == args2);
   assert(args2 == args3);
   assert(args3 == args4);
   assert(args4 == args5);

   gsapi_init_with_args(nullptr, args1.size(), args1.data());
}

输出:

arg 0=foo
arg 1=bar
arg 2=baz

答案 4 :(得分:0)

尝试const_cast it:

gsapi_init_with_args(instance, argc, const_cast<char**>(argv));

也许它有助于修复警告。

答案 5 :(得分:0)

受到n.m。的C ++ 14版本的启发,这里是C ++ 11版本。诀窍是使用一个评估的空lambda表达式来生成一个新类型,以便W__的每个实例都是唯一的。

template <typename T, int N> static char * W__(const char (&src)[N], T) {
   static char storage[N];
   strcpy(storage, src);
   return storage;
}

#define W(x) W__(x, []{})

char * argv[] = {
   W("foo"),
   W("bar")
};

static返回类型前面的W__表示W__具有内部链接,并且不会使用额外符号对目标文件进行膨胀。它与static前面的storage无关,因为后者表示局部变量的静态存储持续时间。下面的代码完全有效,但当然做错了事并且有不确定的行为:

template <typename T, int N> static char * BAD(const char (&src)[N], T) {
   char storage[N];
   strcpy(storage, src);
   return storage;
}

由于必须评估lambda,因此您不能简单地将其类型设为模板参数:

template<typename> void G();
G<decltype([]{})>(); // doesn't work