C++, Wrapper function for sprintf_s - c++

after including banned.h (one of microsoft security tools), the compiler gives me an warning that sprintf() function is not safe, and MSDN center gives me a suggestion to use sprintf_s, since my project is cross platform, I wrote a wrapper for sprintf function.
//safe function for sprintf();
void WrapperSprintf( char *buffer, const char *format, ... )
{
#ifdef _WIN32
sprintf_s(buffer, sizeof(buffer), format,...);
#else
sprintf(buffer, format, ...);
#endif
}
it gives me an error at line sprintf_s(buffer, sizeof(buffer), format,...);
error C2059: syntax error : '...'
Anyone knows how to write a wrapper function for sprintf_s()?
Thanks a lot.

The ... doesn't magically translate from the function declaration down to the other calls using those parameters. You have to include the variable arguments stuff and use that to call the next level down.
The steps are basically:
include the stdarg header.
declare a va_list.
call va_start.
call one of the v*printf functions.
call va_end.
For example, here's a little program that demonstrates how to provide a beast which writes the formatted output to a string, similar to what you seem to be after:
#include <stdio.h>
#include <stdarg.h>
void x (char *buf, char *fmt, ...) {
va_list va;
va_start (va, fmt);
vsprintf (buf, fmt, va);
va_end (va);
}
int main (void) {
char buff[100];
x (buff, "Hello, %s, aged %d", "Pax", 40);
printf ("%s\n", buff);
return 0;
}
Me, I tend to ignore Microsoft's suggestions about sprintf being unsafe. It's only unsafe if you don't know what you're doing and that can be said of any tool. If you want to become a good C programmer, you will learn the limitations and foibles of the language.
Including the one where you use sizeof on a char*, expecting it to return the size of the buffer it points to rather than the size of a pointer :-)
But, if you want to be a C++ developer, be a C++ developer. While C and C++ share a lot of commonality, they are not the same language. C++ includes a lot of C stuff primarily so that you can (mostly) take already-written C code and use it in your C++ applications.
In other words, if it's a C++ application, use std::string and std::stringstream(a) rather than char arrays and s*printf calls.
You should be writing your C++ code as if the C bits didn't exist. Otherwise, you're more a C+ programmer than a C++ one :-)
(a) Of course, knowledgeable developers will probably already be steering clear of the verbosity inherent in the stringstream stuff, and be using something like fmtlib (with the conciseness of printf but with the type safety C++ developers have come to appreciate).
Especially since it's being bought into C++20 where it will be part of the base, available to everyone.

Related

c++ call fmt Libary from C Code with va_args

I'd like to use the fmt libary as part of a debugging framework.
However our project is mixed c and c++.
fmt works nice with c++ however in c we have printf() like format strings.
//c readable header
#ifdef __cplusplus
extern "C"
{
#endif
void Foo(char* format, ...);
#ifdef __cplusplus
}
#endif
//impl.cpp
void Foo(char* format, ...)
{
va_list aptr;
va_start(aptr, format);
//pass aptr to fmt lib somehow
va_end(aptr);
}
Therefore I have to "hide" the C++ part from C code.
The only way (I know) in c how to do formatting is using va_args. However they will not work with fmt templates since the first one is executed at run time and the other one at compile time
=>So the Question
Do you have an idea how to use the fmt lib from C Code?
Thx for your Input :)
You can call formatting functions from C via a wrapper that uses dynamic_format_arg_store to construct argument lists at runtime. However, for that to work you'll need to know all argument types so it won't work with varargs which don't preserve the type information.

Does MinGW-w64 g++ expose microsoft ucrt's implementation of C11 `getenv_s` function? If yes, what header to be included?

According to cppreference.com, getenv_s gets supported since C11
errno_t getenv_s( size_t *restrict len, char *restrict value,
rsize_t valuesz, const char *restrict name );
With MinGW-w64 8.1, g++ reports an error with both #include with cstdlib and stdlib.h
use of undeclared identifier 'getenv_s'; did you mean '_wgetenv_s'?
errcode = getenv_s(&envsize, NULL, 0, name);
^~~~~~~~
_wgetenv_s
I wonder why MinGW-w64 g++ seems not to expose microsoft ucrt's C11 getenv_s?
In c++, do we already have a portable way to retrieve environment variables safely?
In c++, do we already have a portable way to retrieve environment variables safely?
You can use getenv. If don't want to have a raw pointer to a C string owned by someone else leaking into your code you can use std::optional:
#include <cstdlib>
#include <optional>
std::optional<std::string> get_env(const char* env) {
auto t = std::getenv(env);
if (t) return t;
return {};
}
Full example.
PS: even if it was available in C++, I am not sure if I would use getenv_s. resitrct is not standard C++, and passing arrays and their size seperately isnt very idiomatic C++. To my understanding getenv_s is an improvement to getenv in C where you need to deal with nullpointers and string lenghts somehow while in C++ we have different solutions available (std::string for variable length strings and std::optional for optional values).
Edit:
The originally amended answer below is not entirely correct. Currently there is no declaration for getenv_s in <sec_api/stdlib_s.h> on MinGW-w64 implementations, but you can declare it yourself:
#ifdef __cplusplus
extern "C" {
#endif
#include <sec_api/stdlib_s.h> /* errno_t, size_t */
errno_t getenv_s(
size_t *ret_required_buf_size,
char *buf,
size_t buf_size_in_bytes,
const char *name
);
/*
* You can omit this section if you don't want to use the safer
* C++ template function in your C++ code.
*/
#ifdef __cplusplus
extern "C++" {
template <size_t size>
getenv_s(
size_t *ret_required_buf_size,
char (&buf)[size],
const char *name
) { return getenv_s(ret_required_buf_size, buf, size, name); }
}
#endif
#ifdef __cplusplus
}
#endif
On MSVC, you'd still just use #include <stdlib.h> as getenv_s is declared there.
There are also several other C++ template functions missing from the <sec_api/stdlib_s.h> already, presumably due to lack of need, and the lack of declaration for getenv_s entirely is perhaps just something nobody needed as getenv worked just fine.
It's worth mentioning that there is a Windows-only function called _dupenv_s that is much easier to use in place of getenv_s, and you'd just free the memory using the standard free function. It is declared in <sec_api/stdlib_s.h>, so you can use it without issue.
Amended original answer:
At the time of this answer, MinGW-w64 built from source allows you to enable or disable the exposure of the secure CRT functions by default, but even when enabled, it does not appear to mark most standard C functions with secure replacements as "deprecated" the way that Visual C++'s CRT headers do (actually, it appears that it does mark some of them as deprecated, but the macros expand to nothing in the build I'm using).
To more directly address the question, the MinGW-w64 implementation currently stores the prototypes for the secure CRT functions in a separate header file in the sec_api directory, and that header file is not included from the standard C header, which means the corresponding C++ header <cstdlib> will not declare the functions either since it includes only the standard header.
Instead, you need to explicitly include the C headers you need such as <sec_api/stdlib_s.h>, <sec_api/stdio_s.h>, etc., which will only declare the functions if the secure API has been enabled (i.e. MINGW_HAS_SECURE_API is defined to 1 in _mingw.h). As the functions are likely available for linking, you can just use #define MINGW_HAS_SECURE_API 1 before any includes to enable the use of the secure functions that are declared, or declare the functions yourself in the event that they're undeclared.
I feel it's worth mentioning that many, though not all, of the C++-only template functions such as
// #include <sec_api/string_s.h> in MinGW-w64.
// #include <string.h> (C) or <cstring> (C++) in MSVC.
template <size_t size>
errno_t strcpy_s(
char (&dest)[size],
const char *src
);
are declared and implemented.
Based on examples in Microsoft's documentation and preprocessor output in the case of MinGW-w64, both implementations place the secure functions in the global C++ namespace, not the std namespace (e.g. strcpy_s in its fully qualified form is ::strcpy_s) as they're not standard C++ functions.

How to format a C-string platform independently with va_lists?

If I compile my code containing vsnprintf on VS2015 it complains about :
warning C4996: 'vsnprintf': This function or variable may be unsafe.
Consider using vsnprintf_s instead.
If i use vsnprintf_s then gcc fails compiling it.
How to solve this? I would like to compile the code without (suppressed) warnings and platform independent.
Using C++ streams is not possible because the va_list and format string is created in C-Code.
Well, write MS-specific code:
#ifndef _MSC_VER
#define vsnprintf_s(buf, size, count, format, list) std::vsnprintf(buf, size, format, list)
#endif // _MSC_VER
char buf[64];
vsnprintf_s(buf, sizeof(buf), _TRUNCATE, "%s...", valist);

How to call my_function before every time sprintf is called?

sprintf is an API provided by platform. I want to filter some format when it is used. My idea is:
#include <stdio.h>
int my_sprintf(...)
{
my_filter_function(...);
return ::sprintf(...);
}
#define sprintf my_sprintf
Then put these code in pch.
But I am still worrying it can't cover all usages, some one is in prebuilt library and not every project has a pch. Do you have any other idea?
Thanks. It's on windows.
You can't "overwrite" a built-in function. Furthermore, using a macro to replace its name results in your program having undefined behaviour.
So, don't even try to change the behaviour of the standard library. Really, that way madness lies.
Just call my_sprintf from your own code and let the platform do what it always did.
You want to use variadic functions.
Example:
int my_sprintf(char *buffer, char *fmt, ...)
{
int ret;
va_list args;
va_start(args, fmt);
/* insert your filter here */
/* you CAN NOT re-use a va_list variable after being used */
ret = vsprintf(buffer, fmt, args);
va_end(args);
return ret;
}
Note: You are not allowed to define a function / macro with the same name as a function from the standard library. It's undefined behaviour.
You have to replace all your calls to sprintf with your custom my_sprintf function.
You can use namespace concept to define functions with the same names
#include <stdio.h>
namespace myns
{
int sprintf(...)
{
my_filter_function(...);
return ::vsprintf(...);
}
}
than call
char buffer[256];
myns::sprintf(buffer, "Hello, %s!\n", "World");

Relax void * casting in C++

In C, it's not an error to cast pointers to and from void *.
A major obstacle in porting to C++ is the need to cast pointers when returning from functions dealing with generic pointers such as malloc, and functions declared in my own code such as void *block_get(Blkno const blkno);.
My code however is intended to be compiled by C and C++ compilers successfully. If I provide explicit casts everywhere for the sake of C++, they must be C-style casts and I may be masking bugs due to casting non-pointer types to and from pointer types from both languages.
My reference error is the following:
struct Cpfs *cpfs = calloc(1, sizeof(*cpfs));
which in MSVC produces:
Error 2 error C2440: 'initializing' : cannot convert from 'void *' to 'Cpfs *' e:\src\cpfs\cpfs.c 179
Evidently I can't use new or static_cast which I'd naturally use if I was no longer using C. What's the best way to provide maximum type safety surrounding void *for each language with minimal verbosity?
I'd suggest either simply using C style casts, or wrapping the cast in a macro that either expands to nothing (in C), or a static_cast in C++.
If your compiler supports decltype(), you can use some macro magic to avoid having to explicitly repeat the type name (and, thanks to sizeof, the element size):
#ifdef __cplusplus
#define my_calloc(VAR, COUNT) \
static_cast<decltype(VAR)>(std::calloc(COUNT, sizeof *VAR))
#else
#define my_calloc(VAR, COUNT) calloc(COUNT, sizeof *VAR)
#endif
Example usage:
#ifdef __cplusplus
#include <cstdlib>
#else
#include <stdlib.h>
#endif
struct Cpfs *cpfs = my_calloc(cpfs, 42);
The cleaner solution would probably be to just use a C compiler and link the object files, though...
make a replacement allocator function that you can define differently for C and C++ builds :- Something like this in a header file:
#ifdef __cplusplus
template<typename TypeT>
TypeT* MyAlloc(TypeT** pOut,size_t cb){
*pOut = static_cast<TypeT*>(malloc(cb)); //aint c++ pretty.
return *pOut;
}
#else
extern void* MyAlloc(void** ppv, size_t cb);
#endif
Now you have, in c++ builds, a function that can infer the type of thing its dealing with, and in C builds, its a regular function that returns a void*.
The only problem is the need to pass in the pointer to allocate - the c++ compiler wont try to deduce a template parameter based only on the return type of a function afaik. So you could call it agnostically like this :-
int *p;
if(MyAlloc(&p,sizeof(int)*n)){
...
Maybe something like this? (untested, no compiler available, not using macros very often):
#ifdef __cplusplus
#define pointer_cast(type, pointer) reinterpret_cast<type>(pointer)
#else
#define pointer_cast(type, pointer) (type)(pointer)
#endif
The only solution I know is to do explicit casting:
struct Cpfs *cpfs = (Cpfs*)calloc(1, sizeof(*cpfs));
Here both compilers are satisfied.
Also that remember, that for older compilers malloc may return char*.
hth
Mario