Поскольку это DLL, проблема может заключаться в разных кучах, используемых для распределения и освобождения (попробуйте построить библиотеку статически и проверить, будет ли это работать).
Проблема в том, что DLL и шаблоны не согласны вместе очень хорошо. В общем, в зависимости от привязки среды выполнения MSVC это может быть проблемой, если память выделена в исполняемом файле и освобождена в DLL и наоборот (потому что у них могут быть разные кучи). И это может произойти с шаблонами очень легко, например: вы push_back () в вектор внутри removeWhiteSpaces () в DLL, поэтому векторная память выделяется внутри DLL. Затем вы используете выходной вектор в исполняемом файле, и как только он выходит из области видимости, он освобождается, но внутри исполняемого файла, чья куча ничего не знает о куче, из которой она была выделена. Bang, вы мертвы.
Это можно обойти, если и DLL, и исполняемый файл используют одну и ту же кучу. Чтобы обеспечить это, DLL и исполняемый файл должны использовать динамическую среду выполнения MSVC, поэтому убедитесь, что обе они связаны с динамической динамикой, а не статически. В частности, exe должен быть скомпилирован и связан с / MD [d] и библиотекой с / LD [d] или / MD [d], а также с / MT [d]. Обратите внимание, что впоследствии компьютеру, на котором будет запущено приложение, потребуется запустить исполняемую библиотеку MSVC (например, путем установки «Распространяемого на Visual C ++» для конкретной версии MSVC).
Вы могли бы получить эту работу даже с / MT, но это сложнее - вам нужно будет предоставить некоторый интерфейс, который позволит также освободить объекты, выделенные в DLL. Например, что-то вроде:
__declspec(dllexport) void deallocVector(std::vector<std::string> &x);
void deallocVector(std::vector<std::string> &x) {
std::vector<std::string> tmp;
v.swap(tmp);
}
(однако это не очень хорошо работает во всех случаях, так как это нужно вызывать явно, поэтому он не будет называться, например, в случае исключения - для правильного решения , вам нужно будет предоставить некоторый интерфейс из DLL, который будет охватывать вектор под капотом и позаботится о правильном RAII)
EDIT: окончательное решение было фактически состоять в том, чтобы все проектов (exe, dll и весь проект googleTest), встроенных в многопоточную Debug DLL (/ MDd) (по умолчанию проекты GoogleTest встроены в многопоточную отладку (/ MTd))
Нет. Объекты и имена живут в разных измерениях. Один объект может иметь много имен во время его жизни, и невозможно определить, какой из них может быть тем, который вы хотите. Даже здесь:
class Foo(object):
def __init__(self): pass
x = Foo()
два имени обозначают один и тот же объект (self
, когда __init__
работает, x
в глобальной области).
Как и многие другие, это невозможно сделать должным образом. Однако, вдохновленный jsbueno's, у меня есть альтернатива его решению.
Как и его решение, я проверяю кадр стека вызывающих, что означает, что он работает только правильно для вызываемых Python абонентов (см. примечание ниже). В отличие от него, я проверяю байт-код вызывающего абонента напрямую (вместо загрузки и разбора исходного кода). Используя Python 3.4 + 's dis.get_instructions()
, это можно сделать с некоторой надеждой на минимальную совместимость. Хотя это все еще какой-то хакерский код.
import inspect
import dis
def take1(iterator):
try:
return next(iterator)
except StopIteration:
raise Exception("missing bytecode instruction") from None
def take(iterator, count):
for x in range(count):
yield take1(iterator)
def get_assigned_name(frame):
"""Takes a frame and returns a description of the name(s) to which the
currently executing CALL_FUNCTION instruction's value will be assigned.
fn() => None
a = fn() => "a"
a, b = fn() => ("a", "b")
a.a2.a3, b, c* = fn() => ("a.a2.a3", "b", Ellipsis)
"""
iterator = iter(dis.get_instructions(frame.f_code))
for instr in iterator:
if instr.offset == frame.f_lasti:
break
else:
assert False, "bytecode instruction missing"
assert instr.opname.startswith('CALL_')
instr = take1(iterator)
if instr.opname == 'POP_TOP':
raise ValueError("not assigned to variable")
return instr_dispatch(instr, iterator)
def instr_dispatch(instr, iterator):
opname = instr.opname
if (opname == 'STORE_FAST' # (co_varnames)
or opname == 'STORE_GLOBAL' # (co_names)
or opname == 'STORE_NAME' # (co_names)
or opname == 'STORE_DEREF'): # (co_cellvars++co_freevars)
return instr.argval
if opname == 'UNPACK_SEQUENCE':
return tuple(instr_dispatch(instr, iterator)
for instr in take(iterator, instr.arg))
if opname == 'UNPACK_EX':
return (*tuple(instr_dispatch(instr, iterator)
for instr in take(iterator, instr.arg)),
Ellipsis)
# Note: 'STORE_SUBSCR' and 'STORE_ATTR' should not be possible here.
# `lhs = rhs` in Python will evaluate `lhs` after `rhs`.
# Thus `x.attr = rhs` will first evalute `rhs` then load `a` and finally
# `STORE_ATTR` with `attr` as instruction argument. `a` can be any
# complex expression, so full support for understanding what a
# `STORE_ATTR` will target requires decoding the full range of expression-
# related bytecode instructions. Even figuring out which `STORE_ATTR`
# will use our return value requires non-trivial understanding of all
# expression-related bytecode instructions.
# Thus we limit ourselfs to loading a simply variable (of any kind)
# and a arbitary number of LOAD_ATTR calls before the final STORE_ATTR.
# We will represents simply a string like `my_var.loaded.loaded.assigned`
if opname in {'LOAD_CONST', 'LOAD_DEREF', 'LOAD_FAST',
'LOAD_GLOBAL', 'LOAD_NAME'}:
return instr.argval + "." + ".".join(
instr_dispatch_for_load(instr, iterator))
raise NotImplementedError("assignment could not be parsed: "
"instruction {} not understood"
.format(instr))
def instr_dispatch_for_load(instr, iterator):
instr = take1(iterator)
opname = instr.opname
if opname == 'LOAD_ATTR':
yield instr.argval
yield from instr_dispatch_for_load(instr, iterator)
elif opname == 'STORE_ATTR':
yield instr.argval
else:
raise NotImplementedError("assignment could not be parsed: "
"instruction {} not understood"
.format(instr))
Примечание: C-реализованные функции не отображаются в виде фреймов стека Python и поэтому скрыты для этого скрипта. Это приведет к ложным срабатываниям. Рассмотрим функцию Python f()
, которая вызывает a = g()
. g()
является C-реализованным и вызывает b = f2()
. Когда f2()
пытается найти назначенное имя, он получит a
вместо b
, потому что сценарий не обращает внимания на функции C. (По крайней мере, так я думаю, что это сработает: P)
Пример использования:
class MyItem():
def __init__(self):
self.name = get_assigned_name(inspect.currentframe().f_back)
abc = MyItem()
assert abc.name == "abc"
Это невозможно сделать обычно, хотя это может быть достигнуто с помощью интроспекции и средств, предназначенных для отладки программы. Код должен выполняться из файла «.py», хотя, а не только скомпилированного байт-кода или внутри заархивированного модуля, поскольку он полагается на чтение исходного кода файла, из метода, который должен найти «где это running ".
Фокус в том, чтобы получить доступ к кадру выполнения, в котором объект был инициализирован, - с помощью inspect.currentframe - объект фрейма имеет значение« f_lineno », в котором указывается номер строки, где вызов объекта метод (в данном случае __init__
). Функция inspect.filename позволяет извлекать исходный код для файла и извлекать соответствующий номер строки.
Наивный анализ затем заглянет в часть, предваряющую знак «=», и предполагает, что это переменная, которая будет содержать объект.
from inspect import currentframe, getfile
class A(object):
def __init__(self):
f = currentframe(1)
filename = getfile(f)
code_line = open(filename).readlines()[f.f_lineno - 1]
assigned_variable = code_line.split("=")[0].strip()
print assigned_variable
my_name = A()
other_name = A()
Это не будет работать для нескольких назначений, выражений, составляющих объект до создания assignemtn, объектов, добавляемых к спискам или добавляемых к словарям или наборам, экземпляра объекта в инциализации петель for
, и Бог знает, что больше ситуаций - и имейте в виду, что после
Botton line: возможно , но как игрушка - он не может быть использован i производственный код - просто нужно, чтобы имя переменной было передано как строка во время инициализации объекта, так же, как это нужно делать при создании collections.namedtuple
. «Правильный путь» для этого, если вам нужно имя , заключается в том, чтобы явно передать имя инициализации объекта в качестве параметра строки, например, в:
class A(object):
def __init__(self, name):
self.name = name
x = A("x")
И все же, если абсолютно необходимо вводить имя объекта только один раз, есть еще один путь - читайте дальше. Из-за синтаксиса Python некоторые специальные назначения, не использующие оператор «=», позволяют объекту знать, что ему присвоено имя. Таким образом, другие statemtns, которые выполняют назначающие функции в Python, являются ключевыми словами for, with, def и class. Возможно, это злоупотребление, так как specfically создание класса и определение функции являются операторами присваивания, которые создают объекты, которые «знают» их имена.
Давайте сосредоточимся на утверждении def
. Он обычно создает функцию. Но с помощью декоратора вы можете использовать «def» для создания любого объекта - и имя, используемое для функции, доступной для конструктора:
class MyObject(object):
def __new__(cls, func):
# Calls the superclass constructor and actually instantiates the object:
self = object.__new__(cls)
#retrieve the function name:
self.name = func.func_name
#returns an instance of this class, instead of a decorated function:
return self
def __init__(self, func):
print "My name is ", self.name
#and the catch is that you can't use "=" to create this object, you have to do:
@MyObject
def my_name(): pass
(Последний способ сделать это можно использовать в производственном коде, в отличие от того, который прибегает к чтению исходного файла)
Вот простая функция для достижения желаемого результата, предполагая, что вы хотите получить имя переменной, где экземпляр назначен из вызова метода:
import inspect
def get_instance_var_name(method_frame, instance):
parent_frame = method_frame.f_back
matches = {k: v for k,v in parent_frame.f_globals.items() if v is instance}
assert len(matches) < 2
return matches.keys()[0] if matches else None
Вот пример использования:
class Bar:
def foo(self):
print get_instance_var_name(inspect.currentframe(), self)
bar = Bar()
bar.foo() # prints 'bar'
def nested():
bar.foo()
nested() # prints 'bar'
Bar().foo() # prints None
Да, возможно *. Однако проблема сложнее, чем кажется на первый взгляд:
Несмотря на это, знание способов поиска имен объекта иногда может быть полезно для целей отладки - и вот как это сделать:
import gc, inspect
def find_names(obj):
frame = inspect.currentframe()
for frame in iter(lambda: frame.f_back, None):
frame.f_locals
obj_names = []
for referrer in gc.get_referrers(obj):
if isinstance(referrer, dict):
for k, v in referrer.items():
if v is obj:
obj_names.append(k)
return obj_names
Если вы «когда-либо соблазнялось основать логику вокруг имен ваших переменных, приостановить на мгновение и подумать, может ли редизайн / рефакторинг кода решить проблему. Необходимость восстановления имени объекта из самого объекта обычно означает, что базовые структуры данных в вашей программе нуждаются в переосмыслении.
*, по крайней мере, на Cpython