C/C++ build a generic stack frame to call different callback functions - c++

I'm refactoring a C++ application that refers to a bunch a multiple callback functions, each having a different number of arguments and I would like to know if there's a generic way to build a dedicated argument stack frame before calling each of them.
In other words: fetching all required arguments first according to function to be called, then perform a call that would be transparent for called function.
In a way, this is the opposite of a variadic function, as this would be a single function that knows it can receive different number of arguments. Here I have a bunch of immutable, regular functions and I'd like to call them from a generic hub.
I place both C and C++ tags here because I'm interested in propositions for both these languages, I have the feeling that what would work for C would also be applyable to C++ and I'm still open to "C++ only" solutions such as variadic templates or such.
The background of all of it is that quoted program is actually parsing a command line, then calling a function according to the name of the command, passed as first argument after program name, then all required ones as plain strings, but depending of invoked command.
I know I could write something like:
if(nb_of_args == 1)
my_callback_ptr(argv[n]);
else if (nb_of_args == 2)
my_callback_ptr(argv[n],argv[n+1]);
else if (nb_of_args == 3)
my_callback_ptr(argv[n],argv[n+1],argv[n+2]);
else if (nb_of_args == 4)
my_callback_ptr(argv[n],argv[n+1],argv[n+2],argv[n+3]);
else if…
…to restrict the compiled calls to the sole number of arguments regardless of the function itself, but I'd still like to do better.
Thanks in advance to everybody.

A viable approach is to implement dispatch table that will contain pointers to wrapper functions automatically passing appropriate amount of arguments to each callback.
#include <array>
#include <utility>
#include <cstddef>
template<auto x_p_callback, ::std::size_t... x_indexes>
void invoke_callback(char const * const * const pp_args, ::std::index_sequence<x_indexes...>)
{
(*x_p_callback)(pp_args[x_indexes]...);
}
template<typename x_Callback >
struct
t_ArgsCount;
template<typename x_Result, typename... x_Args>
struct
t_ArgsCount<x_Result (x_Args...)>
: ::std::integral_constant<::std::size_t, sizeof...(x_Args)>
{};
template<auto x_p_callback>
void callback_sunk(char const * const * const pp_args)
{
using
t_Callback = typename ::std::remove_reference<decltype(*x_p_callback)>::type;
using
t_Sequence = typename ::std::make_index_sequence<t_ArgsCount<t_Callback>::value>;
invoke_callback<x_p_callback>(pp_args, t_Sequence{});
}
using
t_Callback = void (char const * const * const pp_args);
template<auto... x_p_callbacks>
constexpr auto make_table(void)
{
return ::std::array<t_Callback *, sizeof...(x_p_callbacks)>{&callback_sunk<x_p_callbacks>...};
}
void my_callback_0() {}
void my_callback_1(char const *) {}
void my_callback_2(char const *, char const *) {}
void my_callback_3(char const *, char const *, char const *) {}
int main(int argc, char const * const * const pp_args)
{
constexpr auto table{make_table<my_callback_0, my_callback_1, my_callback_2, my_callback_3>()};
table.at(argc)(pp_args);
}
online compiler

Although I have some doubts about the C code I wrote (all to be verified). To solve the difficulty to convert your callbacks with a prototype like callback(int argc, char *argv[] (as the C/C++ main), my idea has been to make the pointers into the functions table that indexes the callbacks as pointers to variadic functions.
In this way we may call all callback functions passing the maximum number of parameters by means the pointers in the table. Obviously the called functions will only use the parameters they need and not the others.
On the other hand, this code will consume instructions and, depending on the case, even stack memory to pass useless parameters to functions that do not need them.
This code seems to run correctly, but it has to be verified!
#include <stdio.h>
#define CBCALL(cbidx, argv, n) \
ft[cbidx].f(argv[n],argv[n+1],argv[n+2],argv[n+3],argv[n+4])
int cb0();
int cb1(char *);
int cb2(char *, char *);
int cb3(char *, char *, char *);
int cb4(char *, char *, char *, char *);
int cb5(char *, char *, char *, char *, char *);
typedef int FN(char *,...);
struct table {
FN * f;
} ft[]={
{(FN *)cb0},
{(FN *)cb1},
{(FN *)cb2},
{(FN *)cb3},
{(FN *)cb4},
{(FN *)cb5}
};
int main(int argc, char * argv[])
{
if (argc<7) {
CBCALL(argc-1,argv,1);
if (argc>2)
{// Trying with copied parms and NULL.
char *xarg[5];
xarg[0]=argv[1];
xarg[1]=argv[2];
xarg[2]=NULL;
xarg[3]=NULL;
xarg[4]=NULL;
CBCALL(2,xarg,0);
}
} else {
printf("Max 5 parameters are allowed!\n");
}
return 0;
}
int cb0()
{
printf("No parms\n");
return 0;
}
int cb1(char *a)
{
printf("%s\n",a);
return 1;
}
int cb2(char *a, char *b)
{
printf("%s %s\n",a,b);
return 2;
}
int cb3(char *a, char *b, char *c)
{
printf("%s %s %s\n",a,b,c);
return 3;
}
int cb4(char *a, char *b, char *c, char *d)
{
printf("%s %s %s %s\n",a,b,c,d);
return 4;
}
int cb5(char *a, char *b, char *c, char *d, char *e)
{
printf("%s %s %s %s %s\n",a,b,c,d,e);
return 5;
}

Related

function pointers with void* argument casting

I have no idea on how to look this up, even the title is confusing, even I am confused about what I'm looking for, and the question has for sure already been asked but it's so specific to be found, so here a bit of context:
int comparison(const int* a, const int* b) {
return *a - *b;
}
int main(int argc, char const *argv[])
{
int arr[3] = {1,6,-2};
qsort(arr,3,sizeof(int),comparison);
return 0;
}
Well, it does work, but the compiler gives me a warning, because qsort wants a function of type:
int(*)(const void*, const void*)
and comparison is a function of type:
int(*)(const int*, const int*)
I want to know why the compiler is not happy because it just has to cast the address. It should even be happy to give a type to a void* pointer. Is this really bad? Like an undefined behavior or something? Or just the compiler whining about nothing much?
After other reasons already given, there's another one. Historically there were platforms for which void * and int * had different bit arrangements, I've heard rumor of one where void * and int * were different sizes. That function pointer cast won't always work.
const int *ia = (const int *)a;
const int *ib = (const int *)b;
might not compile away to ia = a; but rather to something like ia = a >> 1; So there's really got to be a place for those instructions to be.
why he (the compiler) is not happy .
qsort() expects a function point of type int (*)(const void *a, const void *b), not int (*)(const int *a, const int *b). The compiler could guess its OK and beform a cast, yet it is more productive for the compiler to warn about such problems.
Or just the compiler whining about nothing much?
By warming you, you are allowed to determine the degree of the problem.
In addition to #Alex Reynolds good answer, note that *a - *b may overflow, resulting in the wrong comparison.
Instead:
int comparison(const void *a, const void *b) {
const int *ia = (const int *)a;
const int *ib = (const int *)b;
return (*ia > *ab) - (*ia < *ib);
}
Good compilers recognize the (p>q) - (p<q) idiom and emit efficient code.
The qsort function takes as one of its arguments a function of a type you are not passing. So you'll need to change that.
Inside the comparator, you can recast the pointers to the desired type.
Additionally, you need to dereference the values of the const int pointers you are passing into the comparator function:
#include <stdio.h>
#include <stdlib.h>
static int
comparator(const void *a, const void *b)
{
return *(const int *)a - *(const int *)b;
}
static void
printArr(int arr[], int n)
{
int i;
for (i = 0; i < n; ++i) {
printf("%d ", arr[i]);
}
}
int
main(int argc, const char **argv)
{
int arr[3] = {1, 6, -2};
qsort(arr, 3, sizeof(int), comparator);
printArr(arr, 3);
return EXIT_SUCCESS;
}

How to define pointer to pointer to function and how to use it in C++?

My question is how to translate the following example? Is this a function, that returns int pointer?
int* (*function)(int, (int (*k)(int *)));
And can I can't write program that use it?
Thanks in advance!
It is a function-pointer
The function returns a pointer to an int
The function's first arg is an int
The function's second arg is a function-pointer k
k returns an int
k takes a pointer to an int as argument
Sure you can use that in your program. It is not too unusual. There are much worse declarations i have seen.
I renamed your "function" to "F" for clarity. Then you can write:
int* (*F)(int, int (*kFunc)(int *) );
Alternative:
typedef int (*kFunc)(int *);
int* (*F)(int, kFunc);
There are a lot of ways to use pointer to a function, may be a pattern such as Factory could take advantage of the function pointer to create new objects.( Look here : http://www.codeproject.com/Articles/3734/Different-ways-of-implementing-factories)
May be this piece of code could help you and give ideas of how powerfull can be working with function pointers.
#include <stdio.h>
#include <stdlib.h>
#include <map>
// Define the func ptrs
typedef void (*TFunc)(const char *, int);
typedef int (*TFunc2)(int);
int return_value(int i)
{
return i * 5;
}
void a( const char *name, int i )
{
printf ("a->%s %d\n\n", name, i);
}
void b( const char *name, int i)
{
printf ("b->%s %d\n\n", name, i);
}
struct test
{
const char *name;
int i;
TFunc func;
};
static test test_array[2] =
{
{ "a", 0, a },
{ "b", 1, b },
};
int main(int argc, char **argv, char** envp)
{
// Check the simple case, pointer to a function
TFunc fnc = a;
TFunc2 fnc2 = return_value;
fnc("blabla", 5);
fnc = b;
fnc("hello!", 55);
printf ("%d\n\n",fnc2(5));
//Check arrays of structs when there is a pointer to a fnc
test_array[0].func(test_array[0].name, test_array[0].i);
test_array[1].func(test_array[1].name, test_array[1].i);
//Handle a map of functions( This could be a little implementation of a factory )
typedef std::map<int, TFunc > myMap;
myMap lMap;
lMap.insert(std::make_pair(5, a));
lMap.insert(std::make_pair(2, b));
if( lMap.find( 5 ) != lMap.end() )
{
lMap[5]("hello map 5", 1);
}
myMap::iterator lItFind = lMap.find(2);
if( lItFind != lMap.end() )
{
lItFind->second("hello map 2", 2);
}
return(0);
}
I hope that this helps you.
You should remove extra parentheses, this is correct version:
int* (*function)(int, int (*k)(int *));
explanation (using right-left rule):
int* (*fn)(int, int (*k)(int *));
fn : fn is a
(*fn) : pointer
(*fn)(int, int (*k)(int *)) : to a function taking as arguments
- an int and
- function pointer
which takes a pointer to int
and returns int
int* (*fn)(int, int (*k)(int *)) : and returns a pointer to int
below is a short example on how to use it, also you ask for How to define pointer to pointer to function so below this is also included.
http://coliru.stacked-crooked.com/a/d05200cf5f6397b8
#include <iostream>
int bar(int*) {
std::cout << "inside bar\n";
return 0;
}
int* foo(int, int (*k)(int *)) {
std::cout << "inside foo\n";
k(nullptr);
return nullptr;
}
int main() {
int* (*function)(int, int (*k)(int *));
function = foo;
function(0, bar);
// Now, as you asked for, a pointer to pointer to above function
decltype(function) *pff;
pff = &function;
(*pff)(0, bar);
}

Array of C strings, initialized with string literals

The Ghostscript interpreter API has a function
GSDLLEXPORT int GSDLLAPI gsapi_init_with_args(void *instance, int argc, char **argv)
The final argument argv is a pointer to an array of C strings, which are interpreted as command-line arguments. I obviously cannot change the signature of the function gsapi_init_with_args to take a const char ** argument instead.
If I were willing to ignore (or silence) the deprecated conversion from string constant to 'char*' warning, then I would write simply
char *gs_argv[] = {"", "-dNOPAUSE", "-dBATCH", ...};
and pass gs_argv as the final argument. But I would prefer to fix my code so that I am not relying on an external function to behave in the way I expect it to (and effectively treat gs_argv as const char**).
Is there any simple way to declare gs_argv as an array of pointers to (non-const) C strings, and initialize its elements with string literals? (That is, using a similar approach to how I can initialize a single C string: using char c_str[] = "abc".) The best I can think of is to use
const char *gs_argv0[] = {"", "-dNOPAUSE", "-dBATCH", ...};
and then copy the contents, element by element, into gs_argv.
Please note that I understand why the compiler gives this warning (and have read the answers to, among others, this question). I am asking for a solution, rather than an explanation.
You can use:
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)
Create copies of the string literals using strdup. This is more verbose, but fixes the warning.
char* gs_argv0[NARGS];
gs_argv0[0] = strdup("");
gs_argv0[1] = strdup("-dNOPAUSE");
// ...
Note that you will also need to free the memory allocated by strdup if you want to prevent leaks.
You might also want to add a comment to your code saying why you are doing this, to make it clear for future readers.
If you can guarantee that the function will not modify the non-const parameter, then it is acceptable to use const_cast in this situation.
A C++14 solution.
#define W(x) \
(([](auto& s)->char* \
{ \
static char r[sizeof(s)]; \
strcpy (r, s); \
return r; \
})(x))
char* argv[] =
{ W("--foo=bar",
W("baz"),
nullptr
};
Since this code requires C++11, there's a lower cost C++11 solution in another answer below. I'm leaving this one for posterity.
There are pretty much two choices: ignore it and const_cast, or do the right thing. Since this is modern C++, you're supposed to have nice, RAII classes. Thus, the simplest, safest thing to do is to safely wrap such an array.
// 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;
}
};
Let's see how it works:
#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());
}
Output:
arg 0=foo
arg 1=bar
arg 2=baz
Try to const_cast it:
gsapi_init_with_args(instance, argc, const_cast<char**>(argv));
Maybe it will help with fixing warning.
Inspired by n.m.'s C++14 version, here's a C++11 version. The trick is to use an evaluated empty lambda expression to generate a fresh type, so that each instantiation of W__ is unique.
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")
};
The static in front of W__'s return type means that W__ has internal linkage and won't bloat the object file with extra symbols. It has nothing to do with the static in front of storage, as the latter indicates the static storage duration for the local variable. The code below would be perfectly valid, but of course doing the wrong thing and having undefined behavior:
template <typename T, int N> static char * BAD(const char (&src)[N], T) {
char storage[N];
strcpy(storage, src);
return storage;
}
Since a lambda has to be evaluated, you can't simply make its type a template argument:
template<typename> void G();
G<decltype([]{})>(); // doesn't work

C++ & the C arg_list functions

I need to emulate a library type that allows treating it both as char* & std::string. e.g. this should be allowed:
char c[16];
MyString ms;
...
strcpy(c, ms);
strcpy(c, ms.c_str());
I only control the library but unfortunately can't change this library usage. I cannot change the printf call, just the type sent to it!
I can do this for instance by implementing:
class MyString : public string {
public:
operator const char*();
};
The problem is with this:
sprintf(c, "%s", ms);
The C "arg_list" functions will not use the char* cast.
Any suggestions?
Thanks.
You can pass only trivially-copyable PODs to ....
You could do
struct wrapper { const char * it; };
wrapper it{"hello"};
printf("%s", it);
But the problem is, you cannot manage memory. Once you try to add constructor and destructor to alloc/free the memory, you will be faced with an error.
error: cannot pass objects of non-trivially-copyable type ‘class mystr’ through ‘...’
You can help yourself with:
const char * c_str(const char * it) { return it; }
const char * c_str(const std::string& it) { return it.c_str(); }
const char * cstr = "hello";
std::string cppstr = "world";
printf("%s %s!", c_str(cstr), c_str(cppstr));
Or use boost::format (or similar for ostream). And you should use ostream/format for C++ (type-safe).
You said:
The problem is with this:
sprintf(c, "%s", ms);
The C "arg_list" functions will not use the
char* cast.
There is no cast here. sprintf(..., "%s") is not casting ms to anything, so it will never call your conversion operator. A cast would be: sprintf(c, "%s", (const char *)ms). Unlike strcpy, va_list doesn't cause a coercion as there is no parameter type.
You can pass objects to va_list functions, but it you need to either explicitly cast the arguments to call the correct type conversion, or use .c_str()
If printf() could implicitly handle C++ objects per its format specifiers, it would certainly already be in the language, since it is probably one of the most desired library functions that didn't map over from C.
If you are set on using printf() and family, simply write:
printf("%s", foo.c_str());
Or try something like this (as suggested by #firda in a comment):
const char * c_str(const std:string &s) { return s.c_str(); }
const char * c_str(const char * s) { return s; }
Then you consistently write:
printf("%s%s", c_str(foo), c_str(bar));
Normal C++ variadic functions cannot work with object instances because the values must be bit-copiable (the called function will not know the types of the objects passed at compile time).
With C++11 you can however write a variadic template that takes care of doing the trick of using the call-site type knowledge to unpack the parameters:
#include <string>
#include <stdio.h>
#include <string.h>
int mysprintf(char *buf, const char *fmt) {
return sprintf(buf, "%s", fmt);
}
template<typename T, typename... Args>
int mysprintf(char *buf, const char *fmt, const T& x, Args... args) {
const char *sp = strchr(fmt, '%');
if (sp) {
while (fmt < sp) *buf++ = *fmt++;
std::string fmt1(fmt, fmt+2);
int sz = sprintf(buf, fmt1.c_str(), x);
return sz + mysprintf(buf+sz, fmt+2, args...);
} else {
fputs("Invalid format string\n", stderr);
std::abort();
return 0;
}
}
template<typename... Args>
int mysprintf(char *buf, const char *fmt, const std::string& x, Args... args) {
const char *sp = strchr(fmt, '%');
if (sp) {
if (sp[1] != 's') {
fputs("Invalid format string (%s expected)\n", stderr);
std::abort();
}
while (fmt < sp) *buf++ = *fmt++;
std::string fmt1(fmt, fmt+2);
int sz = sprintf(buf, fmt1.c_str(), x.c_str());
return sz + mysprintf(buf+sz, fmt+2, args...);
} else {
fputs("Invalid format string\n", stderr);
std::abort();
return 0;
}
}
int main() {
char buf[200];
std::string foo = "foo";
const char *bar = "bar";
int x = 42;
mysprintf(buf, "foo='%s', bar='%s', x=%i", foo, bar, x);
printf("buf = \"%s\"\n", buf);
return 0;
}
NOTE: to keep things simple this code doesn't handle any formatting options or % escaping, but allows using %s for both naked char * and std::string.
NOTE2: I'm not saying this is a good idea... just that's possible

How can I portably call a C++ function that takes a char** on some platforms and a const char** on others?

On my Linux (and OS X) machines, the iconv() function has this prototype:
size_t iconv (iconv_t, char **inbuf...
while on FreeBSD it looks like this:
size_t iconv (iconv_t, const char **inbuf...
I would like my C++ code to build on both platforms. With C compilers, passing a char** for a const char** parameter (or vice versa) typically emits a mere warning; however in C++ it's a fatal error. So if I pass a char**, it won't compile on BSD, and if I pass a const char** it won't compile on Linux / OS X. How can I write code that compiles on both, without resorting to trying to detect the platform?
One (failed) idea I had was to provide a local prototype that overrides any provided by the header:
void myfunc(void) {
size_t iconv (iconv_t, char **inbuf);
iconv(foo, ptr);
}
This fails because iconv needs C linkage, and you cannot put extern "C" within a function (why not?)
The best working idea I've come up with is to cast the function pointer itself:
typedef void (*func_t)(iconv_t, const char **);
((func_t)(iconv))(foo, ptr);
but this has the potential to mask other, more serious errors.
If what you want is just to turn a blind eye to some const issues, then you can use a conversion which blurs the distinction, i.e. makes char** and const char** interoperable:
template<class T>
class sloppy {};
// convert between T** and const T**
template<class T>
class sloppy<T**>
{
T** t;
public:
sloppy(T** mt) : t(mt) {}
sloppy(const T** mt) : t(const_cast<T**>(mt)) {}
operator T** () const { return t; }
operator const T** () const { return const_cast<const T**>(t); }
};
Then later in the program:
iconv(c, sloppy<char**>(&in) ,&inlen, &out,&outlen);
sloppy() takes a char** or a const char* and converts it to a char** or a const char*, whatever the second parameter of iconv demands.
UPDATE: changed to use const_cast and call sloppy not a as cast.
You can disambiguate between the two declarations by inspecting the signature of the declared function. Here's a basic example of the templates required to inspect the parameter type. This could easily be generalized (or you could use Boost's function traits), but this is sufficient to demonstrate a solution for your specific problem:
#include <iostream>
#include <stddef.h>
#include <type_traits>
// I've declared this just so the example is portable:
struct iconv_t { };
// use_const<decltype(&iconv)>::value will be 'true' if the function is
// declared as taking a char const**, otherwise ::value will be false.
template <typename>
struct use_const;
template <>
struct use_const<size_t(*)(iconv_t, char**, size_t*, char**, size_t*)>
{
enum { value = false };
};
template <>
struct use_const<size_t(*)(iconv_t, char const**, size_t*, char**, size_t*)>
{
enum { value = true };
};
Here's an example that demonstrates the behavior:
size_t iconv(iconv_t, char**, size_t*, char**, size_t*);
size_t iconv_const(iconv_t, char const**, size_t*, char**, size_t*);
int main()
{
using std::cout;
using std::endl;
cout << "iconv: " << use_const<decltype(&iconv) >::value << endl;
cout << "iconv_const: " << use_const<decltype(&iconv_const)>::value << endl;
}
Once you can detect the qualification of the parameter type, you can write two wrapper functions that call iconv: one that calls iconv with a char const** argument and one that calls iconv with a char** argument.
Because function template specialization should be avoided, we use a class template to do the specialization. Note that we also make each of the invokers a function template, to ensure that only the specialization we use is instantiated. If the compiler tries to generate code for the wrong specialization, you'll get errors.
We then wrap usage of these with a call_iconv to make calling this as simple as calling iconv directly. The following is a general pattern showing how this can be written:
template <bool UseConst>
struct iconv_invoker
{
template <typename T>
static size_t invoke(T const&, /* arguments */) { /* etc. */ }
};
template <>
struct iconv_invoker<true>
{
template <typename T>
static size_t invoke(T const&, /* arguments */) { /* etc. */ }
};
size_t call_iconv(/* arguments */)
{
return iconv_invoker<
use_const<decltype(&iconv)>::value
>::invoke(&iconv, /* arguments */);
}
(This latter logic could be cleaned up and generalized; I've tried to make each piece of it explicit to hopefully make it clearer how it works.)
You can use the following:
template <typename T>
size_t iconv (iconv_t i, const T inbuf)
{
return iconv(i, const_cast<T>(inbuf));
}
void myfunc(void) {
const char** ptr = // ...
iconv(foo, ptr);
}
You can pass const char** and on Linux/OSX it will go through the template function
and on FreeBSD it will go directly to iconv.
Drawback: it will allow calls like iconv(foo, 2.5) which will put compiler in infinite recurrence.
#ifdef __linux__
... // linux code goes here.
#elif __FreeBSD__
... // FreeBSD code goes here.
#endif
Here you have ids of all operating systems. For me it doesn't have any point to try doing something what depends on operating system without checking this system. It's like buying green trousers but without looking at them.
You've indicated that using your own wrapper function is acceptable. You also seem to be willing to live with warnings.
So, instead of writing your wrapper in C++, write it in C, where you'll only get a warning on some systems:
// my_iconv.h
#if __cpluscplus
extern "C" {
#endif
size_t my_iconv( iconv_t cd, char **restrict inbuf, ?* etc... */);
#if __cpluscplus
}
#endif
// my_iconv.c
#include <iconv.h>
#include "my_iconv.h"
size_t my_iconv( iconv_t cd, char **inbuf, ?* etc... */)
{
return iconv( cd,
inbuf /* will generate a warning on FreeBSD */,
/* etc... */
);
}
How about
static void Test(char **)
{
}
int main(void)
{
const char *t="foo";
Test(const_cast<char**>(&t));
return 0;
}
EDIT: of course, the "without detecting the platform" is a bit of a problem. Oops :-(
EDIT 2: ok, improved version, maybe?
static void Test(char **)
{
}
struct Foo
{
const char **t;
operator char**() { return const_cast<char**>(t); }
operator const char**() { return t; }
Foo(const char* s) : t(&s) { }
};
int main(void)
{
Test(Foo("foo"));
return 0;
}
What about:
#include <cstddef>
using std::size_t;
// test harness, these definitions aren't part of the solution
#ifdef CONST_ICONV
// other parameters removed for tediousness
size_t iconv(const char **inbuf) { return 0; }
#else
// other parameters removed for tediousness
size_t iconv(char **inbuf) { return 0; }
#endif
// solution
template <typename T>
size_t myconv_helper(size_t (*system_iconv)(T **), char **inbuf) {
return system_iconv((T**)inbuf); // sledgehammer cast
}
size_t myconv(char **inbuf) {
return myconv_helper(iconv, inbuf);
}
// usage
int main() {
char *foo = 0;
myconv(&foo);
}
I think this violates strict aliasing in C++03, but not in C++11 because in C++11 const char** and char** are so-called "similar types". You aren't going to avoid that violation of strict aliasing other than by creating a const char*, set it equal to *foo, call iconv with a pointer to the temporary, then copy the result back to *foo after a const_cast:
template <typename T>
size_t myconv_helper(size_t (*system_iconv)(T **), char **inbuf) {
T *tmpbuf;
tmpbuf = *inbuf;
size_t result = system_iconv(&tmpbuf);
*inbuf = const_cast<char*>(tmpbuf);
return result;
}
This is safe from the POV of const-correctness, because all iconv does with inbuf is increment the pointer stored in it. So we're "casting away const" from a pointer derived from a pointer that was non-const when we first saw it.
We could also write an overload of myconv and myconv_helper that take const char **inbuf and messes things about in the other direction, so that the caller has the choice whether to pass in a const char** or a char**. Which arguably iconv should have given to the caller in the first place in C++, but of course the interface is just copied from C where there's no function overloading.
Update: now I see that it is possible to handle it in C++ without autotools, yet I'm leaving the autoconf solution for people looking for it.
What you're looking for is iconv.m4 which is installed by gettext package.
AFAICS it's just:
AM_ICONV
in configure.ac, and it should detect the correct prototype.
Then, in the code you use:
#ifdef ICONV_CONST
// const char**
#else
// char**
#endif
I am late to this party but still, here is my solution:
// This is here because some compilers (Sun CC) think that there is a
// difference if the typedefs are not in an extern "C" block.
extern "C"
{
//! SUSv3 iconv() type.
typedef size_t (& iconv_func_type_1) (iconv_t cd, char * * inbuf,
size_t * inbytesleft, char * * outbuf, size_t * outbytesleft);
//! GNU iconv() type.
typedef size_t (& iconv_func_type_2) (iconv_t cd, const char * * inbuf,
size_t * inbytesleft, char * * outbuf, size_t * outbytesleft);
} // extern "C"
//...
size_t
call_iconv (iconv_func_type_1 iconv_func, char * * inbuf,
size_t * inbytesleft, char * * outbuf, size_t * outbytesleft)
{
return iconv_func (handle, inbuf, inbytesleft, outbuf, outbytesleft);
}
size_t
call_iconv (iconv_func_type_2 iconv_func, char * * inbuf,
size_t * inbytesleft, char * * outbuf, size_t * outbytesleft)
{
return iconv_func (handle, const_cast<const char * *>(inbuf),
inbytesleft, outbuf, outbytesleft);
}
size_t
do_iconv (char * * inbuf, size_t * inbytesleft, char * * outbuf,
size_t * outbytesleft)
{
return call_iconv (iconv, inbuf, inbytesleft, outbuf, outbytesleft);
}