[CPP] Simultaneous replacement

Started by
7 comments, last by Julian90 16 years, 9 months ago
I need to perform automatic one-step simultaneous replacement in my code. I am provided with a bit of C code (I don't have it yet: my program will have access to it when the user provides it). This code may include other code files. The user also provides two sets of symbols: a set of symbols within the code (for instance, Foo and Bar) and another set of symbols which must be visible outside the code (for instance, Bar and Baz). I need to generate a bit of C code in response, where all occurences of Foo are replaced by Bar, and all occurences of Bar have been replaced by Baz. Note that an occurence of Foo should not be turned into Baz! I had though of tacking preprocessor directives around the code:
#define Foo Bar
#define Bar Baz

// The provided code

#undef Foo
#undef Bar
However, for obvious reasons (due to the fact that the two definitions above turn Foo into Baz, this does not work. Is there a way to make this work using preprocessor directives? What other approaches would you consider?
Advertisement
How about not messing with sanity

Then again, I don't claim to understand the problem entirely. Isn't is usual to define a public API for this very purpose?

The namespace transform you apparently require is not bijective, so without extra context, it's not possible.
I'm not sure how you can acomplish this using the preprocessor, but you can write a program that does the replacement:

for each symbol    if symbol needs to be replaced        replace it with the corresponding symbol


But, this is the obvious way, so I must be missing something...
The following works (vs2005) yet I don't know if this is guaranteed to work?
struct Foo{};struct Bar{};struct Baz{};typedef Bar Bar_;#define Bar Baz#define Foo Bar_int main(int argc, char *argv[]){	Bar a;	Foo b;	return 0;}
Quote:Original post by Antheus
Then again, I don't claim to understand the problem entirely. Isn't is usual to define a public API for this very purpose?


The problem with a public API is that collisions are possible (and often, they are desired, for instance in the case of polymorphism). The objective of this program is to resolve collisions between different files by renaming the public symbols in one file to differ from the public symbols in another.

Quote:The namespace transform you apparently require is not bijective, so without extra context, it's not possible.


Well, it is bijective, but it doesn't need to be inversed...

Quote:Original post by Gage64
I'm not sure how you can acomplish this using the preprocessor, but you can write a program that does the replacement


This would more or less amount to writing my own preprocessor (since the program would have to follow #include statements, doing all the nasty #ifdef work to isolate actual code, and so on). If at all possible, I would rather prefix and suffix code...

Quote:Original post by dmail


This would solve the problem for types. However, this still leaves functions and global variables.
Doing the preprocessing yourself should not be difficult. In Python (is your implementation language restricted? If so, why?), more or less off the top of my head (i.e. not tested):

import redef regex_escape(x):  return ['\\', ""][x.isalnum()] + xdef construct_regex(keys):  return re.compile('|'.join(["".join([regex_escape(x) for x in key])                               for key in keys))def process_text(text, replacements)  return construct_regex(replacements).sub(lambda x: replacements[x], text)if __name__ == '__main__':  import sys  fname = sys.argv[1]  # Build replacment list from sys.argv[2:], treating each adjacent pair  # of args as a key and value.  replacements = sys.argv[2:]  assert not len(replacements) % 2 # cheap error check.  replacements = dict([(replacements(2 * i), replacements(2 * i + 1))                       for i in range(len(replacements) / 2)])  file(fname + '.out').write(process_text(file(fname).read(), replacements)
Quote:Original post by ToohrVyk
Quote:Original post by dmail


This would solve the problem for types. However, this still leaves functions and global variables.

Maybe something like this:
int foo;int bar;int baz;int &bar_ = bar;#define bar baz#define foo bar_int main(int argc, char *argv[]){	foo=1;	bar=2;	return 0;}


And simmilarly:
void foo();void bar();void baz();void (* const bar_)(void) = bar;//Assign bar to a global const function pointer#define bar baz#define foo bar_int main(int argc, char *argv[]){	foo();	bar();	return 0;}


This is all untested.

EDIT: The first of these will only work if you are using C++ (even if the library is written in C)
Will the output be passed to the compiler immediately or will it be kept for later editing, distribution, etc? If it's going directly to the compiler then just do a second pass with the preprocessor, and if under linux it could be done with a fairly straight-forward shell script.
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The only verdict is vengeance; a vendetta held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily, this vichyssoise of verbiage veers most verbose, so let me simply add that it's my very good honor to meet you and you may call me V.".....V
Quote:This would more or less amount to writing my own preprocessor (since the program would have to follow #include statements, doing all the nasty #ifdef work to isolate actual code, and so on). If at all possible, I would rather prefix and suffix code...


Well you wouldn't actually have to write it yourself, Boost.wave provides a fully standard compliant preprocessor with a programmable iterator interface, for example heres a modified example from the Boost documentation to preprocess a file and replace all occurrences of Foo with Bar and Bar with Baz printing the result to std::cout.

#include <iostream>#include <fstream>#include <string>#include <vector>#include <boost/wave.hpp>#include <boost/wave/cpplexer/cpp_lex_token.hpp>    // token class#include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer classint main(){    //  Open and read in the specified input file.    std::ifstream instream("input.c");    std::string instring =         std::string(std::istreambuf_iterator<char>(instream.rdbuf()),                    std::istreambuf_iterator<char>());                typedef boost::wave::cpplexer::lex_token<>                     token_type;    typedef boost::wave::cpplexer::lex_iterator<token_type> lex_iterator_type;    //  This is the resulting context type to use. The first template parameter    //  should match the iterator type to be used during construction of the    //  corresponding context object (see below).    typedef boost::wave::context<std::string::iterator, lex_iterator_type> context_type;    // The preprocessing of the input stream is done on the fly behind the     // scenes during iteration over the context_type::iterator_type stream.    context_type ctx (instring.begin(), instring.end(), "input.c");    // Set include paths to search.    // ctx.add_include_path("...");    // analyze the input file    context_type::iterator_type first = ctx.begin();    context_type::iterator_type last = ctx.end();            while (first != last)     {        if (first->get_value() == "Foo")        {            first->set_value("Bar");        }        else if (first->get_value() == "Bar")        {            first->set_value("Baz");        }        std::cout << first->get_value();        ++first;    }}

This topic is closed to new replies.

Advertisement