2011-10-01 62 views
8

是否有任何類型安全的方式在D中創建string,只使用運行時可用的信息,而不分配內存?在D中創建字符串而不分配內存?

什麼,我可能想要做一個簡單的例子:因此

void renderText(string text) { ... } 

void renderScore(int score) 
{ 
    char[16] text; 
    int n = sprintf(text.ptr, "Score: %d", score); 
    renderText(text[0..n]); // ERROR 
} 

利用這一點,你會得到一個錯誤,因爲text切片並不是一成不變的,而不是string(即immutable(char)[]

我只能想到的解決此三種方式:

  1. 演員切片到string。它有效,但很醜。
  2. 使用切片分配新字符串。這工作,但我寧願不必分配內存。
  3. 更改renderText採取const(char)[]。這在這裏有效,但(a)它很醜,(b)Phobos中的許多函數需要string,所以如果我想以相同的方式使用這些函數,那麼這是行不通的。

這些都不是特別好。我錯過了什麼嗎?其他人如何解決這個問題?

+1

異端,異端!使用'snprintf'! :D – BCS

+2

我認爲#3實際上是正確的解決方案。字符串 - >不可變(char)[]意味着數據不能改變(只要它仍然在某處引用,否則允許GC收集它,AFAIK)。我認爲你無法爲堆棧分配的數據做出這樣的保證。但除非renderText實際上需要在某處存儲文本,否則它應該使用const(char)[]。由於不可變比const強,所以只能在必要時使用。我同意,然而,在phobos中的許多函數不必要地使用字符串而不是const(char)[],這應該在phobos中修復。 – jpf

+0

@jpf:我同意100%。 –

回答

2

結賬assumeUnique from std.exception Jonathan的回答。

+0

這將與assumeUnique接受ref參數(在這種情況下是否爲IIRC不是r值的切片表達式)進行交互? – BCS

+0

我不認爲使用assumeUnique與堆棧分配的空間是有效的。堆棧空間不是唯一的,當renderScore返回並調用另一個函數時,它可以重用堆棧上的相同空間。 - >不唯一,也不_不可變___。請記住,不可變也隱含意味着共享:你真的希望代碼能夠將堆棧分配的數據傳遞給另一個線程嗎? – jpf

+0

你必須這樣做:'auto text2 = text [0..n]; renderText(assumeUnique(text2));'儘管jpf說,這是非常令人討厭的,因爲你仍然有對'text'的寫入權限,這破壞了'immutable'的目的。 –

0

不,你不能創建字符串不分配。你的意思是access?爲避免分配,您必須使用切片或指針來訪問先前創建的字符串。不確定關於轉換,它可能會或可能不會爲新字符串分配新的內存空間。要解決這個問題

+0

OP似乎願意使用堆棧分配,所以我假設他只是試圖避免堆分配。 – BCS

0

一種方法是將可變字符複製到一個新的版本不變切片,然後說:

void renderScore(int score) 
{ 
    char[16] text; 
    int n = sprintf(text.ptr, "Score: %d", score); 
    immutable(char)[16] itext = text; 
    renderText(itext[0..n]); 
} 

但是:

  1. DMD目前不允許這種因一個錯誤。
  2. 你正在創建一個不必要的副本(比GC分配更好,但仍然不是很好)。
+0

你確定這應該起作用嗎?引用TDPL:「在Stone中鑄造一個不變的價值:......它在整個程序執行過程中永遠不會改變」。我仍然認爲這個要求是堆棧分配的數據滿足__never__。它可以用於局部變量,但只有在編譯時初始化它們才能將它們放入靜態數據段而不是堆棧中。但是你的例子仍然需要在運行時設置itext - >堆棧空間。 – jpf

+0

@jpf:不變性和壽命是正交的問題。不變性保證其在使用期內不會改變,但不能保證無限的使用壽命。如果你引用了有限生命的東西,那麼它是由你來確保你不會在解構之外使用它。 –

+0

看起來你是對的。 TDPL在這裏有點不一致,它清楚地表明「只要它初始化,你就可以認爲它已經被永久地燒到存儲它的內存中了,它將永遠不會改變__program__的執行過程,」imho也意味着無限的生命。但在401頁上它說「一個不變的值保證永遠不會改變整個__its__生命週期」 – jpf

6

您有固定的數組char。你想把它傳遞給需要immutable(char)[]的函數。 只有這樣做沒有任何分配就是投。想想看。你想要的是另一種類型的行爲。這就是鑄造所做的。你可以選擇使用assumeUnique來做到這一點,因爲這確實是你正在尋找的演員,但是否真的給你帶來什麼是值得商榷的。它的主要目的是記錄你演員所做的事情是讓演員的演員被視爲immutable,並且沒有其他參考。看看你的例子,這基本上是真實的,因爲它是函數中的最後一件事,但是你是否想要這樣做一般取決於你。鑑於它是一個靜態數組,如果你搞砸了,並且將它傳遞給了一個允許引用它的函數泄漏的函數,那麼它會冒着內存問題的風險,我不確定assumeUnique是最佳選擇。但是,這又取決於你。無論如何,如果您正在進行演員表演(無論是明確的還是與assumeUnique),您需要確定您傳遞給它的函數不會泄漏對您所訪問的數據的引用傳遞給它。如果確實如此,那麼你是在尋求麻煩。

當然,另一種解決方案是更改函數,以便它需要const(char)[],但仍然存在泄漏對傳入數據的引用的風險。所以,仍然需要確定該功能實際上將做什麼。如果它是pure,不會返回const(char)[](或任何可能包含const(char)[]的東西),並且無法通過該函數的任何其他參數泄漏,那麼您是安全的,但如果其中任何一個不是真的,那麼你必須小心。所以,最終,我相信所有使用const(char)[]而不是鑄造到string的人真的會購買你,因爲你不必投。這樣做還是比較好,因爲它避免了搞砸演員的風險(在可以的時候避免演員一般會更好),但是在轉義引用方面你仍然有同樣的擔心。

當然,這也要求您能夠更改函數以擁有所需的簽名。如果你不能這樣做,那麼你將不得不施放。我相信在這一點上,Phobos的基於字符串的函數的大多數已被更改,以便它們在字符串類型上進行模板化。所以,與Phobos相比,這應該不像以前那麼成問題了。有些函數(尤其是std.file中的函數)仍然需要模板化,但最終,需要string的Phobos中的函數最終應該相當少見,並且有充分理由要求它。

然而,最終問題是,你正在試圖將靜態數組當作動態數組,而D肯定會讓你這樣做,你這樣做的風險很大,而你需要確定你使用的函數不會泄漏任何對你傳遞給它們的本地數據的引用。

+0

如何使renderText採取「範圍const(char)[]」? 「範圍存儲類意味着參數中的引用不能被轉義(例如分配給全局變量)。」 – jpf

+0

@jpf:你確定嗎? DMD允許它。 –

+0

這是來自D2語言參考的引用:http://www.digitalmars.com/d/2.0/function.html「功能參數」部分。雖然我在TDPL中找不到類似的東西。但是如果它尚未正確實施,我不會感到驚訝;-) – jpf