Как передать в другую единицу трансляции имя символа?

Рейтинг: 0Ответов: 4Опубликовано: 26.02.2023

Коротко резюмирую вопрос: Нужно получить адрес 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

Ответы

▲ -1Принят

Короткий ответ на мой собственный вопрос "можно ли превратить строковый литерал в имя символа?" - нет, нельзя, потому что C++ статически типизирован, и это противоречит всей этой парадигме.

▲ 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;
▲ 0

Для связи строки и параметра можно схитрить разными способами. Хочу показать один из.

Допустим у нас есть обработчик

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";

Возможно я угадал вашу мысль.

▲ -1

Если нужно сравнивать адреса переменных (в контексте разных единиц трансляции), то нужно чтобы переменные имели внешнее связывание.

Получить адрес internal-linkage переменной, можно только изнутри соответствующей единицы трансляции. Снаружи - никак. Адреса этих переменных вообще не фигурируют в .obj файле, что развязывает руки оптимизатору.

Добиться внешнего связывания для констант можно двумя способами:

  1. либо, явно объявив переменную внешней:
// a.h
extern const int n/*=0*/;
// a.cpp
const int n=0;

это не позволит использовать заранее известное значение переменной в оптимизациях или при вычислении размера статического массива...

  1. Либо, объявить переменную inline, тогда при линвковке будет выбран адрес только одной переменной, как это делается с inline функциями, когда мы берем их адрес (требует C++17):
// a.h
inline const int n=0;