C++ preprocesor macro for accumulating comma-separated strings - c++

I need to do the following:
const char* my_var = "Something";
REGISTER(my_var);
const char* my_var2 = "Selse";
REGISTER(my_var2);
...
concst char* all[] = { OUTPUT_REGISTERED }; // inserts: "my_var1, my_var2, ..."
REGISTER and OUTPUT_REGISTERED are preprocesor macros. This would be great for large number of strings, like ~100. Is it possible to accomplish this?
PS. The code belongs to level-0 "block" – i.e. it is not inside any function. AFAIK, I cannot call regular functions there.

#include <iostream>
#include <vector>
using namespace std;
vector<const char*>& all()
{
static vector<const char*> v;
return v;
}
struct string_register
{
string_register(const char* s)
{
all().push_back(s);
}
};
#define REGISTER3(x,y,sr) string_register sr ## y(x)
#define REGISTER2(x,y) REGISTER3(x,y,sr)
#define REGISTER(x) REGISTER2(x, __COUNTER__)
REGISTER("foo");
REGISTER("bar");
int main()
{
}

Related

Extract the variable name of a pointer as a string

I have a pointer to an int object.
int x = 10;
int *ptr;
ptr = &x;
I want to write a function GetString(int *ptr) which would return me the string ptr. How do I do that in C++?
There isn't a direct way to extract a variable's string name in C++. The language has only rudimentary reflection features, see the type_traits standard library and SO post Why does C++ not have reflection?.
But you can use preprocessor stringification # to hack something similar: define
#define STRINGIFY(x) #x
then the preprocessor substitutes STRINGIFY(ptr) with the string "ptr".
Note: if you want to stringify the result of a macro expansion use two levels of macros.
#include <iostream>
#include <map>
const std::string& getString(int *ptr, const std::map<int*, std::string>& mp){
return mp.find(ptr)->second;
}
int main()
{
std::map<int* , std::string> mp;
int x = 10;
mp.insert({&x, "ptr1"});
int y = 9;
mp.insert({&y, "ptr2"});
std::cout << getString(&y, mp);
}
Demo

How to name an integer with a string name?

I want to have a function making a new integer like this:
void makeInt(string str, int x);
void makeInt(string str, int x) {
int str = x;
}
int main() {
makeInt(name,4);
}
But now the question is, how can I make the string, name the integer? So that int name = 4. Because now it would only give out an error. Is that actually possible?
This is by the way in C++
You can, use C preprocessor macro.
#define makeInt(str, x) int str = x
int main() {
makeInt(name, 4);
}
Other than that, there is no way to do that in C++.
But I can't see any reason why would you want to do that.
If you want to store a variable with name inputted from user, and then retrieve the variable with name inputted from user, consider using std::map<std::string, int>.
#include <map>
#include <iostream>
#include <string>
int main(){
std::map<std::string, int> values;
values["name"] = 4;
std::cout << values["name"];
}
will prints 4.

Autocasting C++ char array as string

Let's say I have a String class that can be constructed with a char array pointer. Is there any crazy way, through some magical operator overload, free function, or preprocessor macro to make Python-like syntax work, autocasting a char array literal to a String? To make this compile:
String a = "Foo".substr(1);
I suppose a wild pre-compile sed statement would do it, but something within the abilities of clang would be preferred.
For C++11 and beyond
#include <iostream>
#include <string>
int main() {
using namespace std::string_literals;
auto a = "foo"s.substr(1);
}
If you wanted to write this for your own String class then the way to get the same behavior would be to roll your own user defined string literal, and then do the same
#include <cstddef>
class Str {
public:
explicit Str(const char*) {}
Str substr(int) { return *this; }
};
Str operator"" _s (const char* input, std::size_t) {
return Str{input};
}
int main() {
auto s = "something"_s.substr(1);
}

Enum in a class with strings

I'm trying to implement a class (C++) with an enum (with the permitted parameters). I got a working solution, but if I try to extend the functionality I get stuck.
Header data_location.hpp
class DataLocation
{
private:
public:
enum Params { model, period };
std::string getParamString(Params p);
};
Program data_location.cpp
string DataLocation::getParamString(Params p){
static const char * ParamsStrings[] = {"MODEL", "PERIOD"};
return ParamsStrings[p];
}
The array ParamsStrings should be generally available in the class, because I need a second method (with inverse function) returning the enum value given a string.
If I try to define the array in the header I get the error:
in-class initialization of static data member ‘const char* DataLocation::ParamsStrings []’ of incomplete type
Why is the type incomplete? The compiler is for sure able to counts the strings in the array, isn't it?
In case there is no way to get my code working, is there an other way? With 1) no XML; 2) no double definition of the strings; 3) not outside the class; 4) no in code programmed mapping.
In class (header) use keyword static and initialize it outside (.cpp) without the static keyword:
class DataLocation {
public:
enum Params { model, period };
string getParamString(Params p);
static const char* ParamsStrings[];
// ^^^^^^
};
const char* DataLocation::ParamsStrings[] = {"MODEL", "BLLBLA"};
//^^^^^^^^^^^^^^^^^^^^^^^^
The code you have posted is perfectly fine.
Here's the proof:
#include <iostream>
#include <string>
struct DataLocation
{
enum Params { model, period };
std::string getParamString(Params p){
static const char * ParamsStrings[] = {"MODEL", "PERIOD"};
return ParamsStrings[p];
}
};
int main()
{
auto a = DataLocation();
std::cout << a.getParamString(DataLocation::model) << std::endl;
return 0;
}
The error message you are getting is not to do with definition of a static data member in an inline function - that's allowed.
There's something else you're not showing us.
The main issue in my question (the second part) was that if I split the class in .hpp and .cpp the definition of the array (I mixed *char and string) has also to be split:
// data_location.hpp
class DataLocation {
static const char * ParamsStrings[];
}
// data_location.cpp
const char * ParamsStrings[] = {"MODEL", "PERIOD"};
At the end I introduced a consistency check to be sure that the number of values in enum growths as the number of strings. Because the array in C++ is somehow limited I had to go for a std::vector (to get the size).
Code for data_location.hpp
#ifndef DATA_LOCATION_HPP_
#define DATA_LOCATION_HPP_
#include <string>
#include "utils/dictionary.hpp"
extern const char* ENV_DATA_ROOT;
struct EDataLocationInconsistency : std::runtime_error
{
using std::runtime_error::runtime_error;
};
struct EDataLocationNotValidParam : std::runtime_error
{
using std::runtime_error::runtime_error;
};
class DataLocation
{
private:
std::string mRootLocation;
static const std::vector<std::string> msParamsStrings;
static bool msConsistenceCheckDone;
public:
DataLocation();
std::string getRootLocation();
std::string getLocation(Dictionary params);
enum Params { model, period, LAST_PARAM};
std::string Param2String(Params p);
Params String2Param(std::string p);
};
#endif
Code for data_location.cpp
#include "data_location.hpp"
#include <string>
#include <cstdlib>
using namespace std;
const char* ENV_DATA_ROOT = "DATA_ROOT";
bool DataLocation::msConsistenceCheckDone = false;
DataLocation::DataLocation() {
mRootLocation = std::getenv(ENV_DATA_ROOT);
if (not msConsistenceCheckDone) {
msConsistenceCheckDone = true;
if (LAST_PARAM+1 != msParamsStrings.size()) {
throw(EDataLocationInconsistency("DataLocation: Check Params and msParamsStrings"));
}
}
}
string DataLocation::getRootLocation() {
return mRootLocation;
}
string DataLocation::getLocation(Dictionary params) {
// to do
return "";
}
const vector<string> DataLocation::msParamsStrings = { "MODEL", "PERIOD", ""};
string DataLocation::Param2String(Params p) {
if (p>=msParamsStrings.size()) {
throw(EDataLocationNotValidParam("Parameter not found"));
}
return msParamsStrings[p];
}
DataLocation::Params DataLocation::String2Param(string p) {
for (int i = 0; i < msParamsStrings.size(); i++) {
if (p == msParamsStrings[i])
return (Params)i;
}
throw(EDataLocationNotValidParam("Parameter not found"));
}
And also a unit test:
#include <boost/test/unit_test.hpp>
#include "data_location.hpp"
#include <string>
using namespace std;
BOOST_AUTO_TEST_SUITE( data_location )
BOOST_AUTO_TEST_CASE(data_location_1) {
DataLocation dl;
auto s = dl.getRootLocation();
BOOST_CHECK_EQUAL(s, "/home/tc/data/forex" );
BOOST_CHECK_EQUAL(dl.Param2String(DataLocation::period),"PERIOD");
BOOST_CHECK_EQUAL(dl.String2Param("PERIOD"),DataLocation::period);
BOOST_CHECK_THROW(dl.String2Param("SOMETHING"), EDataLocationNotValidParam);
BOOST_CHECK_THROW(dl.Param2String((DataLocation::Params)100), EDataLocationNotValidParam);
}
BOOST_AUTO_TEST_SUITE_END()
C++ is very picky about what it will let you initialize inside of a class definition; there are some particularly non-intuitive rules surrounding static members. It all has to do with the ODR, and why all the rules are the way they are is not especially important.
To cut to the chase, making your array a static constexpr const member should shut the compiler up. With the C++11 standard, the restrictions were relaxed a bit, and one of the new stipulations was that static constexpr members can be initialized inline. This is perfect for your application, since the strings in your array are compile-time constants.
The recent g++ compiler which support C++0x or later compiles thus code. Pure C compile compiles, too. Because strings in initialization like {"MODEL", "PERIOD"}; implemented as const char * pointer to the char array.

how to convert single-byte string to wide string in c++?

I know the encoding and that the input string is 100% single byte, no fancy encodings like utf etc. And all I want is to convert it to wchar_t* or wstring basing on a known encoding. What functions to use ? btowc() and then loop ? Maybe string objects have something useful in them. There are lot of examples but all are for "multibyte" or fancy loops with btowc() that only show how to display output on screen that indeed this function is working, I haven't seen any serious example how to deal with buffers in such situation, is always wide char 2x larger than single char string ?
Try this template. It served me very well.
(author unknown)
/* string2wstring.h */
#pragma once
#include <string>
#include <vector>
#include <locale>
#include <functional>
#include <iostream>
// Put this class in your personal toolbox...
template<class E,
class T = std::char_traits<E>,
class A = std::allocator<E> >
class Widen : public std::unary_function<
const std::string&, std::basic_string<E, T, A> >
{
std::locale loc_;
const std::ctype<E>* pCType_;
// No copy-constructor, no assignment operator...
Widen(const Widen&);
Widen& operator= (const Widen&);
public:
// Constructor...
Widen(const std::locale& loc = std::locale()) : loc_(loc)
{
#if defined(_MSC_VER) && (_MSC_VER < 1300) // VC++ 6.0...
using namespace std;
pCType_ = &_USE(loc, ctype<E> );
#else
pCType_ = &std::use_facet<std::ctype<E> >(loc);
#endif
}
// Conversion...
std::basic_string<E, T, A> operator() (const std::string& str) const
{
typename std::basic_string<E, T, A>::size_type srcLen =
str.length();
const char* pSrcBeg = str.c_str();
std::vector<E> tmp(srcLen);
pCType_->widen(pSrcBeg, pSrcBeg + srcLen, &tmp[0]);
return std::basic_string<E, T, A>(&tmp[0], srcLen);
}
};
// How to use it...
int main()
{
Widen<wchar_t> to_wstring;
std::string s = "my test string";
std::wstring w = to_wstring(s);
std::wcout << w << L"\n";
}