Я ищу самый известный метод для статического определения структур данных C / C ++, которые должны быть циклически связаны. Например. дерево, где и ребенок, и родитель нуждаются в указателях друг на друга.
extern struct Op op_subops[4]; // fwd ref to children of ops
struct Op
{
const char *name;
struct Op *parent;
struct Op *children;
};struct Op ops[128] = {
{"op",0,&op_subops[0]}
};
struct Op op_subops[4] = {
{"subop1",&ops[1],0},
{"subop2",&ops[1],0}
};
Выше компилируется (g ++ 5.2). extern
Ключевое слово позволяет мне создать прямую ссылку из ops
в ops_subops
и другое направление работает естественно, так как ops
предшествует ops_subops
,
Мне не нравится то, что я предпочел бы, чтобы оба массива были static
(не создавать публично видимый символ в объектном файле).
Я мог бы использовать целочисленный индекс для одного из направлений, но это выглядит немного странно, и я бы предпочел, чтобы компоновщик разрешил мне адрес.
У кого-нибудь есть волшебное ключевое слово, чтобы сделать эту работу?
Спасибо!
РЕДАКТИРОВАТЬ: мне нужно избегать таких вещей, как статические конструкторы C ++ и новейшие расширения C ++ 17 (к сожалению). И мой подход должен быть независимым от платформы.
В C или C ++ суть в том, что вы не можете ссылаться на объект до того, как объявите его, но оба языка позволяют вам писать «предварительные объявления», которые объявляют имя и (возможно, неполный) тип без определяющий предмет. Будучи более знакомым с C, я отвечу с этой точки зрения; некоторые семантики могут отличаться в C ++.
В C основная проблема с кодом, который вы представили, заключается в том, что он противоречит требованию, что для массивов
Тип элемента должен быть завершен всякий раз, когда указан тип массива.
(C2011, 6.2.5 / 20)
Это означает, что, хотя вы можете объявить массив вперед op_subops
без размера (оставляя его тип незавершенным в этом смысле), вы не можете выдать какое-либо объявление для него перед типом struct Op
определено. Это можно решить, просто поменяв местами две декларации.
Исправив проблему упорядочения, в C вполне нормально объявить оба массива с помощью static
связь; Вы просто должны быть уверены, что если какой-либо объект объявлен более одного раза (т.е. op_subops
) что все декларации согласны с его связью:
struct Op
{
const char *name;
struct Op *parent;
struct Op *children;
};
static struct Op op_subops[4]; // fwd ref to children of ops
static struct Op ops[128] = {
{"op",0,&op_subops[0]}
};
static struct Op op_subops[4] = {
{"subop1",&ops[0],0},
{"subop2",&ops[1],0}
};
Вы можете опустить длину op_subop
если хотите, из первой декларации, и на самом деле я бы порекомендовал вам сделать это, чтобы избежать необходимости согласования длины в двух декларациях.
Хотя вы спрашиваете о «наилучшем [известном] методе», это будет вопрос мнения, окрашенного контекстом, а вопросы мнения здесь не по теме. С другой стороны, вам кажется, что в этом случае вам нужен массив для дочерних элементов, поэтому я не вижу, какие еще могут быть альтернативы без изменения типов.
Поскольку вы ищете объявление статических массивов и не можете принять их как глобальные, я предполагаю, что вы намерены сделать так, чтобы оба массива оставались недоступными для внешнего мира.
Сначала я подумал, что это решение для прямого заявления. Это хорошо скомпилировано с gcc для файла C: онлайн демо 1. Но, к сожалению, он не компилируется ни на MSVC, ни на C ++, потому что это не стандартно:
static struct Op op_subops[]; // compiler dependent - it's standard!
На самом деле все проще: просто объявлять массив с его размером:
static struct Op op_subops[4]; // as simple as that
Последующая инициализация не определяет / переопределяет массив; он обозначает уже объявленный массив для инициализации. Фактически, вы можете оставить измерение массива вне инициализации. И на этот раз он компилируется на gcc и на MSVC:
static struct Op op_subops[] = { // [] or [4]
...
};
Тогда вам больше не придется декорировать элементы как статические: безымянное пространство имен гарантирует, что другие модули компиляции не смогут ссылаться на эти объекты