I agree that using numeric_std should be promoted and std_logic_arith should be deprecated.
I also think that information passed around should retain the appropriate level of detail. Does it make sense to keep converting an address to a std_logic_vector when it really is an unsigned representation of a number?
-- multiplication
signal a : signed(17 downto 0) ;
signal b : signed(12 downto 0) ;
signal c : signed(a'length + b'length - 1 downto 0) ;
c <= a * b ;
-- addition
signal a : signed(7 downto 0) ;
signal b : signed(7 downto 0) ;
signal c : signed(12 downto 0) ;
c <= resize( a, c'length ) + resize( b, c'length ) ;
-- NOTE: resize() will sign or zero extend based on the context it is used
-- ram addressing
signal ram : ram_t ;
signal ram_addr : unsigned(11 downto 0) ;
dout <= ram( to_integer( ram_addr ) ) when rising_edge( clock ) ;
Hope this helps.