views:

169

answers:

4

In C++ I wanted to define a constant that I can use in another function, A short answer on how to do this will be fine..

Lets say at the beginning of my code I want to define this constant:

//After #includes
bool OS = 1; //1 = linux
if (OS) {
  const ??? = "clear";
} else {
  const ??? = "cls";
}

I don't know what type to use to define the "clear" string... I'm so confused.

Later on I want to use it within a function:

int foo() {
 system(::cls); //:: for global

 return 0;
}

How would I define the string up top, and use the string down below? I heard char only had one character and things... I'm not sure how to use , since it says it's converting string into const char or something.

+2  A: 

Here's the problem, you're suffering from going out of scope with the variables. If I declare something within brackets, it only exists within the brackets.

if( foo ){
    const char* blah = "blah";
}

Once we leave the if statement, the variable blah disappears. You'll need to instantiate it non-locally to whatever brackets you write. Hence:

void Bar(){
    const char* blah = "blah";
    if( foo ){
        //blah exists within here
    }
}

However, blah will not exist outside of Bar. Get it?

wheaties
+4  A: 

char* isn't quite a char. char* is basically a string (it's what strings were before C++ came along).

For illustration:

int array[N];  // An array of N ints.
char str[N];   // An array of N chars, which is also (loosely) called a string.

char[] degrades to char*, so you'll often see functions take a char*.

To convert std::string to const char*, you can simply call:

std::string s;
s.c_str()

In this case, it's common to use the preprocessor to define your OS. This way you can use the compiler to do the platform specific stuff:

#ifdef OS_LINUX
const char cls[] = "clear";
#elif OS_WIN
const char cls[] = "cls";
#endif

One thing you may want to consider is making it a function. This avoids nasty dependencies of global construction order.

string GetClearCommand() {
  if (OS == "LINUX") {
    return "clear";
  } else if (OS == "WIN") {
    return "cls";
  }
  FAIL("No OS specified?");
  return "";
}

What it looks like you're trying to do is this:

#include <iostream>
using namespace std;

#ifdef LINUX
const char cls[] = "LINUX_CLEAR";
#elif WIN
const char cls[] = "WIN_CLEAR";
#else
const char cls[] = "OTHER_CLEAR";
#endif

void fake_system(const char* arg) {
  std::cout << "fake_system: " << arg << std::endl;
}

int main(int argc, char** argv) {
  fake_system(cls);
  return 0;
}

// Then build the program passing your OS parameter.
$ g++ -DLINUX clear.cc -o clear
$ ./clear 
fake_system: LINUX_CLEAR
Stephen
Perfect, but within the function that I'm using (within while loops as well..) I need to still call system(cls), with ::cls the compiler fails and says it isn't defined, how would I access the cls constant defined out of scope?
Nullw0rm
I think the thing with `#ifdef` is supposed to be a global (outside of functions). For this usage, I think a plain `const char* cls = "clear"` might also be fine (no need for an array if you just want a reference to a constant literal).
UncleBens
@Jason, edited my answer with an example. You can always look "up-scope" to see variables. Never down-scope.
Stephen
There's a typo in your first example, `char str[N};` should be `char str[N];`.
Javier Badia
@javier, thanks, fixed
Stephen
A: 

Yet another option is to create a class with a bunch of static methods. Create a new method for each command. Something like:

// in sys-commands.h
class SystemCommands {
public:
    static char const* clear();
    static char const* remove();
};

This gives you a few nice options for the implementation. The nicest one is to have a separate implementation file for each platform that you select during compile time.

// in sys-commands-win32.cpp
#include "sys-commands.h"
char const* SystemCommands::clear() { return "cls"; }
char const* SystemCommands::remove() { return "erase /f/q"; }

// in sys-commands-macosx.cpp
#include "sys-commands.h"
char const* SystemCommands::clear() { return "/usr/bin/clear"; }
char const* SystemCommands::remove() { return "/bin/rm -fr"; }

Which file gets compiled will determine which command set will be used. Your application code will look like:

#include <cstdlib>
#include "sys-commands.h"

int main() {
    std::system(SystemCommands::clear());
    return 0;
}

Edit: I forgot to mention that I prefer static functions to global constants for a bunch of reasons. If nothing else, you can make them non-constant without changing their types - in other words, if you ever have to select the command set based on runtime settings, the user code does not have to change or even be aware that such a change occurred.

D.Shawley
A: 

You can use a common header file and link to different modules depending on the systen:

// systemconstants.hpp

#ifndef SYSTEM_CONSTANTS_HPP_INCLUDED
#define SYSTEM_CONSTANTS_HPP_INCLUDED

namespace constants {
   extern const char cls[];  // declaration of cls with incomplete type
}

#endif

In case of Linux, just compile and link to this one:

// linux/systemconstants.cpp

#include "systemconstants.hpp"

namespace constants {
   extern const char cls[] = "clear";
}

In case of Windows, just compile and link to this one:

// windows/systemconstants.cpp

#include "systemconstants.hpp"

namespace constants {
   extern const char cls[] = "cls";
}

System-specific translation units could be placed in specific subdirectories (linux/, windows/, etc) of which one could be automatically selected during the build process. This extends to many other things, not just string constants.

sellibitze