Automatically replace sprintf function with C ++ string functions

We have a lot of "demo" code that uses sprintf and cstrings to create file names. I would like to replace it with C ++ strings in order to resolve very long file names and because it gives cleaner syntax.

So essentially we need to convert blocks like

ofstream some_file; char filename[100]; sprintf(filename,"%s/soln%i.dat",a.c_str(), b); some_file.open(filename); 

into something like

 ofstream some_file((a + "soln" + to_string(b) + ".dat").c_str()); 

However, since this is used in many places, I would like to use some kind of automatic conversion (i.e. sed expressions, emacs function / macroros, visual studio? Etc.). Difficulties (which I see):

  • The contents of the sprintf statement can be anything. (So, do we need to parse the sprintf instruction?)

  • some_file and a file name can be declared anywhere, can be named anything, and are often reused to output to multiple files. (So, do we need to parse C ++?)

Is this possible / possible?

For bonus points, can we avoid calling .c_str () on the line (without changing the stream constructor)?

+4
source share
2 answers

Here's something simpler without Yassnippet:

 (defun sprintf-to-ofstream (file) (interactive "sFile to print to: ") (back-to-indentation) (let* ((start (point)) arglist sprintf-args ofstream-args (end (save-excursion (move-end-of-line 1) (point))) (line (buffer-substring-no-properties start end))) (unless (string= (substring line 0 7) "sprintf") (error "No `sprintf' at this line")) (setq arglist (split-string (substring line (1+ (position ?\( line)) (position ?\) line :from-end t)) "\\s-*,\\s-*") sprintf-args (split-string (substring (cadr arglist) 1 -1) "%[^[:alpha:]%#]*[[:alpha:]%#]") ofstream-args (with-output-to-string (princ (concat "\"" (car sprintf-args) "\"")) (setq sprintf-args (cdr sprintf-args)) (dotimes (i (length sprintf-args)) (princ " + ") (when (< (+ i 2) (length arglist)) (princ (nth (+ i 2) arglist)) (princ " + ")) (princ (concat "\"" (nth i sprintf-args) "\""))))) (kill-region start end) (insert (concat "ofstream " file "((" ofstream-args ").c_str());" )))) 

You can bind it to any key you use and use it like this:

  • Move the point to the line containing sprintf that you want to replace, for example, by running M-% sprintf , then call this function. He will ask you to specify the name of the file in which you want to print the message (maybe I could make it more complex and look up for the place where the stream was declared, but if you say that it may have been declared in an arbitrary place, which sounds like a wasted effort).
  • Press RET , after which the function will change the line to look:
 ofstream foo(("" + a.c_str() + "/soln" + b + ".dat").c_str()); 

considering your original example. Obviously, if you think adding the to_string() arguments around is more likely, then this is easy to do, but with some extra effort it is also easy to avoid joining blank lines, but sometimes this can change the effect / interpretation of the + operator, so I decided to keep it in this way. I will try to do this in the yassnippet script a little later today, so that it would be possible to interactively go to undefined places in the replaced line and have some predefined replacements.

+2
source

Use the Boost Format library. It implements printf formats in C ++, creating C ++ strings as output. The challenge is similar, but slightly different. Your example:

 string fname = boost::format("%s/soln%i.dat") % a % b; ofstream some_file(fname.c_str()); 

Basically, you will need to identify the char buffer and replace it with a string; identify the sprintf operator, add it to the arguments (if quotation marks are followed, etc.) and rewrite it instead of% using the first argument wrapped in boost :: format. It sounds a little complicated, but not impossible. The big problem is that your blocks may not look right, and identifying these situations as a whole will be very difficult.

+2
source

Source: https://habr.com/ru/post/1442769/


All Articles