Возвращая const char *; Насколько уродливая статика? - PullRequest
4 голосов
/ 22 марта 2012

По независящим от меня причинам мне нужно вернуть const char* из функции, но я не знаю, какими должны быть char во время компиляции. Мое решение выглядит примерно так:

const char* __str__() {
  static std::string String;
  String = [some fancy stuff];
  return String.c_str();
}

static предотвращает уничтожение строки при выходе из функции, но это также означает, что память остается в памяти до тех пор, пока моя программа не закроется (верно?). Поскольку возвращаемая строка может иногда быть огромной (ГБ), это может быть реальной проблемой.

Я обычно избегаю указателей любой ценой и использую static только для учеников, так что я не уверен на 100%, что я делаю. Это гарантированно сработает? Есть ли лучший способ?

[Контекст этого вопроса - печать сложного объекта на python с использованием метода __str__. Я определяю метод в своем коде c ++, который затем упаковывается SWIG. Пример SWIG показывает использование static, но мне не ясно, что это единственный способ. Я открыт для предложений.]

Ответы [ 3 ]

10 голосов
/ 22 марта 2012

static представляет дополнительные проблемы помимо области выделения:

  • Функция не реентерабельна
  • Невозможно выполнить очистку, когда вызывающая программа завершена с возвращаемым значением

Есть ли причина не возвращать значение и не позволять вызывающему его освободить?:

const char* __str__() {
    char *s = malloc(2 * 1024 * 1024 * 1024);  // 2 GB
    [some fancy stuff with s];
    return s;
}

...

const char *magic = __str__();
[do something with magic]
free (magic);  magic = NULL;   // all done
5 голосов
/ 22 марта 2012

Как сказал @ Prætorian, SWIG может вернуть std :: string в Python.Вот пример из примера SWIG, я думаю, вы смотрите.Также показан способ избежать использования зарезервированного имени в C ++:

xi

%module x

%{
#include "x.h"
%}

%include <windows.i>
%include <std_string.i>
%rename(__str__) display;
%include "x.h"

xh

#include <sstream>
#include <string>

class Vector
{
public:
    double x,y,z;
    Vector(double a,double b,double c):x(a),y(b),z(c) {}
    ~Vector() {}
#ifdef SWIG
    %extend {
        std::string display()
        {
            std::ostringstream temp;
            temp << '[' << $self->x << ',' << $self->y << ',' << $self->z << ']';
            return temp.str();
        }
    }
#endif
};

Выход

Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import x
>>> v = x.Vector(1.0,2.5,3.0)
>>> print v
[1,2.5,3]
3 голосов
/ 22 марта 2012

Это гарантированно работает со значительными оговорками.

Все предостережения сводятся к двум основным проблемам, которые объединяются таким образом, что это не очень приятно:

  1. В вашей всей программе есть только один экземпляр String.
  2. String не является константой.

Это вызовет у вас множество интересных вопросов. Например, если вы вызовете __str__ еще раз где-нибудь в вашей программе, любой, кто сохранит копию const char *, которую вы ему передали, может в итоге иметь недействительный указатель. И даже если это не так, они в конечном итоге будут содержать указатель на память, которая изменилась. Короче говоря, результатом будет неопределенное поведение.

Другой пример, если вы вызываете __str__ из более чем одного потока, в какой-то момент он взорвется, поскольку оба потока попытаются одновременно изменить String.

К счастью, у вас нет проблемы статического порядка инициализации. String гарантированно будет инициализирован при первом вызове __str__.

Вы можете решить проблему String, оставаясь вечно, позвонив по номеру String.clear() в __str__, если вы уверены, что ни у кого нет никаких const char * s, указывающих на состояние вашего String. String.clear() освободит любое хранилище, которое оно может использовать.

Лично я бы использовал это только как метод последней инстанции. Возможность случайных частей программы, сжимающих указатель, беспокоит меня бесконечно. Нет четкого показателя времени жизни этого указателя, за исключением того факта, что он не может быть гарантированно работать, если __str__ когда-либо будет вызван снова. Но опять же, это может зависеть от того, что именно делает __str__.

Кроме того, __str__ - это плохое имя для использования. Имена, содержащие два последовательных подчеркивания, зарезервированы для реализации C ++.

...