Sign in to follow this  

[CPP] Simultaneous replacement

This topic is 3813 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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 this post


Link to post
Share on other sites
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 this post


Link to post
Share on other sites
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 this post


Link to post
Share on other sites
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 this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 re

def regex_escape(x):
return ['\\', ""][x.isalnum()] + x

def 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 this post


Link to post
Share on other sites
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 this post


Link to post
Share on other sites
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 this post


Link to post
Share on other sites
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 class

int 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;
}
}


Share this post


Link to post
Share on other sites

This topic is 3813 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this