[CPP] Simultaneous replacement

Recommended Posts

ToohrVyk    1595
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?

Share on other sites
Antheus    2409
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.

Share on other sites
Gage64    1235
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...

Share on other sites
dmail    116
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;}

Share on other sites
ToohrVyk    1595
Quote:
 Original post by AntheusThen 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 Gage64I'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.

Share on other sites
Zahlman    1682
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)

Share on other sites
King Mir    2490
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)

Share on other sites
joanusdmentia    1060
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.

Share on other sites
Julian90    736
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;    }}