Как передать в другую единицу трансляции имя символа?
Коротко резюмирую вопрос: Нужно получить адрес internal-linkage переменной из вне, то есть из другой единицы трансляции без геттеров. В файле, где определена internal-linkage переменная, есть функция, которая своим аргументом принимает, например, строковый литерал (имя переменной), и преобразует его в имя идентификатора (адрес internal-linkage переменной). Возможно ли провернуть такое?
Есть main.cpp и a.cpp
В a.cpp есть свои переменные с внутренним связыванием, доступные только в a.cpp и есть шаблонная функция со внешним связыванием, которая доступна во всех единицах трансляции. Эта шаблонная функция (в a.cpp) в качестве аргумента принимает одну из переменных с внутренним связыванием, определенных тоже в a.cpp.
Вопрос: как из main.cpp вызвать ту самую шаблону функцию и передать в качестве аргумента переменную с внутренним связыванием определенную в a.cpp? Нужно указать функции какой идентификатор взять в ее (функции) области видимости, а не файла main.cpp. Возможно ли такое? Адресов переменных мы не знаем, знаем только имена идентификаторов.
// main.cpp:
#include <iostream>
#include "common.h"
using namespace std;
int main()
{
cout << boolalpha
<< (&n == &GetN()) // false
<< endl;
cout << boolalpha
<< (&n == &GetT(n)) // true
<< endl;
return 0;
}
// common.h:
const int n = 0; // internal linkage
const int& GetN(); // external linkage
template<typename T>
T& GetT(T& t) {
return t;
}
// a.cpp:
#include "common.h"
const int& GetN() {
return n;
}
Нужно заменить (&n == &GetN()) на шаблонный аналог. Шаблонная функция из примера выше работает неправильно, так как передает аргументом переменную n из текущей области видимости, а должна передавать n, определенную в файле a.cpp. Результат работы шаблонной функции в примере выше должен быть false.
Нужно что-то типа (&n == &GetT(a.cpp::n)). То есть, мы можем использовать только имя идентификатора, без функции взятия адреса в файле a.cpp
Ответы (4 шт):
Если нужно сравнивать адреса переменных (в контексте разных единиц трансляции), то нужно чтобы переменные имели внешнее связывание.
Получить адрес internal-linkage переменной, можно только изнутри соответствующей единицы трансляции. Снаружи - никак. Адреса этих переменных вообще не фигурируют в .obj файле, что развязывает руки оптимизатору.
Добиться внешнего связывания для констант можно двумя способами:
- либо, явно объявив переменную внешней:
// a.h
extern const int n/*=0*/;
// a.cpp
const int n=0;
это не позволит использовать заранее известное значение переменной в оптимизациях или при вычислении размера статического массива...
- Либо, объявить переменную inline, тогда при линвковке будет выбран адрес только одной переменной, как это делается с inline функциями, когда мы берем их адрес (требует C++17):
// a.h
inline const int n=0;
Свяжите статическую переменную обычной ссылкой в нужном пространстве имён. И тогда можете уверенно достучаться до нужной статической переменной извне.
common.h
namespace acpp {
// ссылается на статическую переменную в файле a.cpp
extern int const & n ;
}
a.cpp
namespace acpp {
// ссылка указывает на именно текущую статическую
int const & n = :: n ;
}
И в main.cpp уже вы точно получите ссылку на нужную статику
cout << boolalpha
<< (&n == &GetT(acpp :: n)) // false
<< endl;
Короткий ответ на мой собственный вопрос "можно ли превратить строковый литерал в имя символа?" - нет, нельзя, потому что C++ статически типизирован, и это противоречит всей этой парадигме.
Для связи строки и параметра можно схитрить разными способами. Хочу показать один из.
Допустим у нас есть обработчик
void AddPropertyHandler(void * MyProp, const char * name) {
// Тут мы уложим MyProp в списочек красиво и инициализируем
}
А как бы теперь так сделать.... что бы список свойств упал в этот список... А можно... Давайте создадим интересный шаблон
template <typename T,char... Chars> class MYT{
T value;
// Далее нужно реализовать implicit и explicit cast, реализовал один из
T& operator = (T val) { value = val; return value; };
// Дальше реализуем конструктор, именно конструктор нам и поможет соиденить
public:
MYT() { /*constructor*/
const char name[sizeof...(Chars)+1] = {Chars...,'\0'}; // Поучаем имя
AddPropertyHandler(this, name); // Делаем проброс
}
};
Теперь.... создаём класс и добавляем N
class MyClass {
public:
MYT<int,"n"> n; // Упростить можно через #define разве что
MYT<int,"a"> a;
MYT<int,"b"> b;
}
Доп хитрость.
MYT<int,"n"> n;
упрощается так
#define D_MYT(x,n) MYT<x,#n> n;
Тогда можно переписать
class MyClass {
public:
D_MYT(int,n)
D_MYT(int,a)
D_MYT(int,b)
}
Класс обязательно нужно создавать конструктором, тогда в AddPropertyHandler попадут все свойства с именами. Так же используя template можно разделить по-типам.
Так же можно применять такие штуки
#define xdef_htmtag(a,b) const int _htag_##a=b; const char* htmltag_##a=#a;
xdef_htmtag(a,1);
// const int _htag_a = 1;
// const char* htmltag_a = "a";
Возможно я угадал вашу мысль.