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");
Related
I have some code implemented in template variadic functions that uses modern c++17 features. Being templates, they are implemented in the .h files.
// .H FILE
template <typename... T>
inline constexpr void foo(const T& ...values){
// Do stuff
}
Is there a way to create a compatibility layer that would allow users to access this functions from C?
The way I actually solved may not be valid to all particular cases!!
I found that trying to pass arguments directly to a c++ variadic function was not possible (to the best of my knowledge). Instead, a void vector would be passed, and the results will not be the ones expected. In this case, stringifying the c input, and passing it to the c++ function worked just fine.
#ifdef __cplusplus
extern "C" {
#endif
void cfoo(const char * fmt, ...)
{
va_list args
va_start(args, fmt);
char str[1024];
vsprintf(str, fmt, args);
cpp::foo(str); // My c++ function
va_end(args);
}
#ifdef __cplusplus
}
#endif
For some reason, when I declare variables and functions with va_list types in them, the Clang AST treats them as __va_list_tag internally. This is problematic since the latter is a builtin definition that isn't included in stdarg.h. Is there a way to tell Clang not to replace this?
Here's some example code:
#include <cstdarg>
typedef int (*process_args)(va_list args);
void useless_func() {
process_args useless_fp;
}
When I replace the types of declarations with their canonical types, I get the following:
#include <cstdarg>
typedef int (*process_args)(va_list args);
void useless_func() {
int (*useless_fp)(__va_list_tag *);
}
I'd ideally like the __va_list_tag * part to be replaced with va_list so the resulting code can be compiled as C++ code.
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.
So I have looked here and here and at a few other links mentioned in the first question and I have the following code already:
The .cpp file:
#include "arp_piping.h"
#include <string>
#include <iostream>
#include <stdio.h>
std::string exec(char* cmd, FILE* pipe) {
pipe = _popen(cmd, "r");
if (!pipe) return "ERROR";
char buffer[128];
std::string result = "";
while(!feof(pipe)) {
if(fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
_pclose(pipe);
return result;
}
The header/linker file:
#ifndef ARP_PIPING_H
#define ARP_PIPING_H
#endif
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
my function goes here something like
EXTERNC .....exec(char* cmd, FILE* pipe) ????
#undef EXTERNC
My question is what goes in the bit above as I am unsure what to be typing. I am trying to call the function in the .cpp file from my C main function int main(int argc, char** argv) {}
To call C++ functions from C you need to do two things. 1) Let the C++ code know it's going to be used by C so that it can generate C-friendly symbols. 2) Hide any functionality that C can't understand.
The first part is easily achieved by simply defining the functions as you would in C (I.E. don't use any C++ only features like namespaces) and then wrapping them in an extern "C" block if C++ is defined. You basically want your header file to contain C-only code, and then just open the extern block at the top, and close it at the bottom of the file (my example will make this more clear).
The second part is a little trickier, but not too difficult. In your case, your function returns a std::string which is a C++ only class. It can not be used in C and therefore either needs to be replaced with something that can be used in C, or it needs to be hidden behind something that C can use. For the sake of argument let's assume you can't replace std::string with say, char*. In this case you need to hide std::string from the C-facing code. The common way of doing this is to use an opaque pointer.
Basically, the C-facing code deals only with a pointer to something. That something it neither knows about, nor cares about. The C++ code is free to use a std::string internally, but must make sure to hide it before interfacing with the C API. In my example, you can see I've provided an opaque pointer to a struct I've called cppstring.
In the source file, cppstring is just a struct that holds a std::string. I've changed your example code to use the new cppstring struct. One important thing to note is that because the C code can only deal with a pointer to a cppstring, we need to create it on the heap in our C++ code and return the pointer to it. This means that we must provide the C users some way of freeing it when they're done, which I've also provided in the example.
Using this technique you can wrap the entirety of std::string behind a C API, allowing C users to use all of the functionality that std::string provides. I've provided an example of wrapping std::string::substr to show you how.
N.B. I haven't compiled nor tested this code and for the sake of simplicity I haven't included any of the relevant header files, etc. Nevertheless, it should be enough to get you started.
// C header
#ifdef __cplusplus
extern "C" {
#endif
typedef struct cppstring *cppstring_p;
cppstring_p exec(char *cmd, FILE *pipe);
void free_cppstring(cppstring_p cppstr);
/* example of wrapping std::string::substr for C users */
cppstring_p substr(cppstring_p str, int pos, int count);
#ifdef __cplusplus
}
#endif
// CPP source
struct cppstring {
std::string data;
cppstring(void) {}
cppstring(std::string const& s) : data(s) {}
};
cppstring_p exec(char *cmd, FILE *pipe) {
pipe = _popen(cmd, "r");
if (!pipe) return "ERROR";
char buffer[128];
auto result = new cppstring;
while(!feof(pipe)) {
if(fgets(buffer, 128, pipe) != NULL)
result->data += buffer;
}
_pclose(pipe);
return result;
}
void free_cppstring(cppstring_p cppstr) {
delete cppstr;
cppstr = nullptr;
}
cppstring_p substr(cppstring_p str, int pos, int count) {
assert(str);
return new cppstring(str->data.substr(pos, count));
}
You need to declare the function as extern "C" in the cpp file:
extern "C" char *exec(char* cmd, FILE* pipe) {
...
}
In the header/linker file you need to declare it's prototype with the keyword "extern", like so:
extern char *exec(char* cmd, FILE* pipe);
Also, are you sure you want to return a c++'s std::string to your C code?
I have a logging function which accepts variadic parameters. This works fine for say android logging and printf, but I want to do the same with std::cout and file streams. Is there an easy way solve this?
void LogManagerImpl::LogInfo(const char* msg, ...)
{
va_list argptr;
va_start(argptr, msg);
/* Log to stdout */
if (mLogToStdOut)
{
#ifdef ANDROID
__android_log_vprint(ANDROID_LOG_INFO, __ENGINE_LOG_TAG, msg, argptr);
#elif defined _WIN32 || _WIN64
//printf ("%s:%s",__ENGINE_LOG_TAG,"INFO:"); vprintf(msg, argptr); printf("\n");
// how do I do the same as above except with for example std::cout?
#endif
}
/* Log to file */
if (mLogToFile)
{
// TODO
}
va_end(argptr);
}
Don't try to use a variadic wrapper for C++ streams, just use the corresponding C API such as vprintf/vnsprintf. Wrapping streams in this way just throws away all the benefits and causes additional complexity.
Why not have your wrapper API use streams, and map them to printf on Android platforms. That way you get all the benefits of streams and only lose them on platforms that don't natively support them.