I'm working with a large C++ project that presently produces 66 different binaries. Each entrypoint contains its own global variables and main() function, though there's a lot of common code that's provided through a shared library.
For ease of distribution, I would like to distribute a single statically-linked binary, like you'd get from a Go or Rust project. Instead of invoking:
./ProgramFoo
./ProgramBar
I'd like to have a single combined binary that "execs" itself into one of those tools behind the scenes based on argv parameters, sort of like how busybox works:
./CombinedProgram ProgramFoo
./CombinedProgram ProgramBar
Look, I get that there's a "right" way to do this — refactor all 66 source files. Replace all global state with class singletons. Replace all the main() functions with entrypoint functions that could be dispatched from a single, unifying main() function. That sounds like a lot of work and a fair amount of disruption to all the other developers on the project. Is there truly no alternative on the compiler/linker level?
(I also don't want to just archive the binaries inside the CombinedProgram, write them to disk, and exec them. Boo. If I wanted a tarball, I'd just use a tarball.)
My understanding of C/C++ binary production is that the compiler will insert a crt0 startup routine that will initialize all my global state and then invoke main() with the appropriate parameters. Could I... perhaps... sneak some code in before that crt0 that peeks at argv and then proceeds down the correct code path?
Is there truly no alternative on the compiler/linker level?
Not really. Refactoring is the best / least work way to accomplish your task.
% cc -o one busy.c ; ln one two ; ./one ; ./two
one
two
The main() needs to look at ARGV[0] to determine how it was called. Then act on that information.
Simplest example:
#include <stdio.h>
#include <string.h>
int one(int argc, char **argv) {
printf("one\n");
return 1;
}
int two(int argc, char **argv) {
printf("two\n");
return 2;
}
int main(int argc, char **argv) {
char *end = argv[0];
int len = strlen(end);
end += len; // jump to the end of the command.
if (argc >= 1) {
if (!strcmp(end-3, "one")) {
return one(argc, argv);
} else if (!strcmp(end-3, "two")) {
return two(argc, argv);
}
}
}
I don't know of a tool that automatically refactors the commands into functions. The alternative is having one file determine what to do and then exec one of the 66 other statically-linked binaries.
Related
Im using gtest as the test framework for my project and I need to use the following parameter value which is passed as a gtest parameter, inside the test case
./gtest_bin --gtest_repeat=5
Currently what I'm doing is, I've created a custom Listener extended from "EmptyTestEventListener" and pass the iteration value to a global variable when the callback to the following function is received as following,
customListener class
class customListener : public testing::EmptyTestEventListener
{
customListener(int *iteration) : m_iteration(iteration) {}
virtual void OnTestIterationStart(const testing::UnitTest& unit_test, int iteration)
{
*m_iteration = iteration;
}
}
main
int g_iteration;
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
if(argc > 1)
g_array_length = atoi(argv[1]);
testing::TestEventListeners& listners = testing::UnitTest::GetInstance()->listeners();
listners.Append(new printers::customListener(&g_iteration));
return RUN_ALL_TESTS();
}
Is there an easier way to get this gtest parameter value?
If generalized, Is there a way to get any gtest specific parameter value that can used inside a testcase?
IMPORTANT NOTE
Please use with great care - as this is not documented feature and
might be changed (i.e. it might stop working) in next gtest/gmock
versions.
You can access any flag by ::testing::GTEST_FLAG(flag_name) - in your case - ::testing::GTEST_FLAG(repeat).
I got this information indirectly from this manual and by examining gtest.h file.
Sorry if the title is misleading, I'm currently looking for solutions to the following:
I'm developing a library, for other people to use. They have to follow a strict design concept and the way they structure any additional features within the library. They all use Linux and (Vim) and as such as are allowed to use terminal commands (i.e to be able to compile etc..) and we all use clang as a compiler.
My question is this: Let's suppose I write a function called: "checkCode":
template<typename T>
void checkCode(T&& codeSnippet)
{
//// code
}
I want to make this function run so whenever they type "checkCode" in a terminal this function is therefore called. I know using clang thy have similar functionality, however, this is understandable as you're using the whole of clang. So:
1) Is it possible to just compile a class, and then access each of the functions through
the .dylab | .so file?
2) Might it be a better idea, or, better to take a copy of the source of clang, add this functionality and role it out to those using and contributing to the library? This would be like an additional add-on to clang?
Thanks
you could use one executable and symbolic links to it like busybox:
int main(int argc, char **argv)
{
string programName = argv[0];
size_t lastSlash = programName.find_last_of('/');
if(lastSlash != string::npos)
programName = programName.substr(lastSlash + 1);
if(programName == "function_1")
{
function_1();
return 0;
}
if(programName == "function_2")
{
function_2();
return 0;
}
// ...
// normal main code
return 0;
}
Working on updating some old code and the original authors decided that all the command line argument variables should be globals. This obviously makes things more challenging, from a testing and development standpoint.
My question is how do I best manage command line arguments that all classes need to use (for example a trace flag / debugging flag). A coworker suggested at the very least wrapping the variables in a namespace, but that just doesn't seem sufficient. I thought about a singleton or static class and just providing getters but that doesn't seem very elegant. On the other hand that seems better than having to pass 5 configuration options to every class that needs to know if debugging and a handful of other options are set though.
The biggest problem with global variables is that changing them from within a function tends to become an unexpected side effect which introduces bugs. In the case of command line arguments, however, they are essentially constants as far as the running process is concerned. The only thing preventing you from declaring them const is that they need to be assigned when you are parsing the command line in the beginning.
I would suggest creating some mechanism that allows you to initialize the arguments in the beginning, but then prevents any part of the program from ever changing them. That will effectively avoid any disadvantage that global variables would normally introduce.
One way might be a ProgramArguments class/struct with const members that are initialized in the constructor, by parsing the command line. You could then have something like:
std::unique_ptr<ProgramArguments const> g_program_arguments;
int main(int argc, char* argv[])
{
g_program_arguments.reset(new ProgramArguments(argc, argv));
if(g_program_arguments->verbose)
std::cout << "verbose!" << std::endl;
// ...
return 0;
}
That wouldn't prevent you from changing the pointer to point to a different ProgramArguments instance however. Another way might be to temporarily cast away the constness for initialization purposes:
struct ProgramArguments {
ProgramArguments() {}
bool verbose;
};
ProgramArguments const g_program_arguments;
void init_program_arguments(int argc, char* argv[])
{
ProgramArguments& program_arguments = const_cast<ProgramArguments&>(g_program_arguments);
program_arguments.verbose = true;
}
int main(int argc, char* argv[])
{
init_program_arguments(argc, argv);
if(g_program_arguments.verbose)
std::cout << "verbose!" << std::endl;
return 0;
}
This will depend on the amount of globals we are talking about. Personally, I think it is fine to have a few globals for things like debug flags and say a singleton Log Manager.
If you really want to respect OOP principles by the book, then you would have to pass everything a function or object needs as parameters. Never accessing global state. As you have mentioned, passing a lot of common parameters to every function gets boring pretty quickly, so one pattern that might help you alleviate this is the Context Object.
I am extending a library for computational fluid dynamics, so I am dealing with legacy code. The applications involve initializing sometimes very large objects, most of which are codependent. The initialization is dependent on configuration and input files stored in a directory.
Trying to use a testing framework compared to my own testing hacked libs should make sense, since there are various test cases and families, and tests, and I could benefit from having the test tree and shiny reports + ability to automatize the tests.
However, I heve encountered a problem when trying to call specific tests at specific points in the program. This problem has occured already when I tried using Google Test - see this question.
Here is a model of the problem using Boost.Test:
#define BOOST_TEST_MODULE hugeObjectEvenTest
#define BOOST_TEST_NO_MAIN
#include <boost/test/included/unit_test.hpp>
#include<random>
#include<iostream>
BOOST_AUTO_TEST_SUITE (hugeObjectEvenTest)
BOOST_AUTO_TEST_CASE (test1)
{
BOOST_CHECK(hugeObject.value() % 2 == 0);
}
BOOST_AUTO_TEST_SUITE_END()
class HugeClass
{
int value_ = 0;
public:
HugeClass() = default;
HugeClass(int x) : value_(x) {};
int value () { return value_; }
void setValue (int val) { value_ = val; }
};
int main(int argc, const char *argv[])
{
HugeClass hugeObject;
std::random_device rd;
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> dist(0,100);
for(int i = 0; i < 10; ++i)
{
hugeObject.setValue(dist(e1));
std::cout << hugeObject.value() << std::endl;
}
return 0;
}
This is merely a model of a numerical solver application, like the one found here.
What I think I need is a global fixture, that is able to take a reference to the hugeObject.
The instances just like hugeObject are modified (modeled with random number generation) during the simulation, within the simulation loop (modeled with the for loop).
All I want to do, is to execute specific tests at specific points within main, and benefit from having a test tree, and all the other perks of using a testing framework. Something similar to the functionality of the Minimal Test Facility.
Is this at all possible with Boost.Test? Like Google Test, selecting specific tests can be done by parsing during execution. This is of no use whatsoever for my problem. I have used both GTest and BoostTest for unit tests, where the initialization of fixtures is local and not dependent on main (argc, argv, configuration and input files), and I had no problems.
Edit: I will get flamed for this probably, but when dealing with legacy code, I believe it would be beneficial to somehow be able to access the objects in main via const refs (to ensure that the tests don't modify objects), in a simpler way than inheriting from fixture classes. In my case, doing that means a day of work, compared to simple BOOST_TEST_REQUIRE placed within main when using the minimal testing framework. Of course, with the minimal framework I have no test tree, etc, so I am back where I started: at my own hacked test lib.
One possible way to do this is to perform your own manual test registration, separate the tests you want to execute together into suites and run them manually. For example:
using namespace boost::unit_test;
void test1() { std::cout << "Running test 1\n"; }
void test2() { std::cout << "Running test 2\n"; }
void test3() { std::cout << "Running test 3\n"; }
void init_test_tree() {
test_suite *ts1 = BOOST_TEST_SUITE( "suite_a");
ts1->add( BOOST_TEST_CASE( &test1 ) );
ts1->add(BOOST_TEST_CASE( &test2 ));
framework::master_test_suite().add(ts1);
test_suite *ts2 = BOOST_TEST_SUITE( "suite_b");
ts2->add( BOOST_TEST_CASE( &test3 ) );
framework::master_test_suite().add(ts2);
}
bool empty_init() { return true; }
int main( int argc, char *argv[] ) {
init_test_tree();
std::cout << "Run suite a\n";
framework::run( framework::master_test_suite().get("suite_a"));
std::cout << "Run suite b\n";
framework::run( framework::master_test_suite().get("suite_b"));
std::cout << "Run the tree\n";
// pass empty initialization function as we've already constructed the test tree
return unit_test_main(&empty_init, argc, argv);
}
Registering your own test cases manually is tedious, boring and error-prone and I don't recommend it. Instead, you can simply define your own main() instead of having Boost.Test provide it for you. Write a main that looks like this:
HugeClass hugeObject;
boost::unit_test::test_suite *init_function(int argc, char *argv[])
{
// create test cases and suites and return a pointer to any enclosing
// suite, or 0.
return 0;
}
int main(int argc, const char *argv[])
{
std::random_device rd;
std::default_random_engine e1(rd());
std::uniform_int_distribution<int> dist(0,100);
for(int i = 0; i < 10; ++i)
{
hugeObject.setValue(dist(e1));
std::cout << hugeObject.value() << std::endl;
}
return boost::unit_test::unit_test_main(init_function, argc, argv);
}
If you do this, you get:
Automatic test case registration
Use of test suites
The ability to do any special stuff first in main() before any part of Boost.Test runs
One annoying side-effect of writing your own main: the signature for the init_function is different depending on if you linked with the static version of Boost.Test or the shared library (dynamic) version of Boost.Test. The differences are discussed in my Boost.Test documentation rewrite in the sections on the static library and the shared library.
I am trying to learn how to make a program in C++ that when you run it, you can tell it to run and specify options all in one line. For example you can do ipconfig /all in CMD and it runs ipconfig.exe with the option /all. Another example would be shutdown -f which tells the computer to shutdown with the option -f. For example, I want to make a program that downloads something from a URL and call it for example downloader. From command line one would type downloader http://filehere.com /h which would download the file with the /h option which I would define its property in my program. I don't want code or guides on how to make a downloader I am just trying to learn how to specify options like the /h. Are there any guides out there that you know of and could post or any sample code? I have tried searching for guides, but I think I just don't know what this operation is actually called. Thank you.
You typically define your main function to take two arguments: int argc and char *argv[], such as:
int
main(int argc, char *argv[])
{
...
The first argument is how many parameters your program received, argv is a pointer to them. Note, this isn't mandated, you can name them whatever you want, but that's the convention. Just make sure your types match up.
You can use an option-parsing library, but those are often OS-specific. One simple way to check if you received a /h is:
int got_h = 0;
for (int i=0; i<argc; ++i)
if (strcmp(argv[i], "/h") == 0)
got_h = 1;
...
if (got_h)
...
argv[argc] will always be NULL to make iterating through them easier.
Some more information here: http://www.site.uottawa.ca/~lucia/courses/2131-05/labs/Lab3/CommandLineArguments.html
The main function takes two arguments, traditionally named argc and argv:
int main (int argc, char * argv[])
{
// ...
}
argc contains the number of arguments passed on the command line, and the argv array contains such arguments (with argv[0] being the name used to invoke your program); the last element of the argv array (i.e. argv[argc]) contains a NULL pointer.
Depending upon your proficiency and inclination to use pointers, you may prefer to capture the command line as a vector<string>:
// UNTESTED CODE
int main(int argc, char **argv) {
std::vector<std::string> args(argv+1, argv+argc);
if(args.empty()) {
std::cout << "Usage: downloader URL [options]\n";
return 1;
}
if(std::find(args.begin(), args.end(), "/h") != args.end()) {
option_h = true;
}
Download(args[0]);
}