Add vect_erase, VECT_ERASE.  VECT_POPBACK needs a dtor

vect_popback and VECT_POPBACK are now defined in terms of vect_erase and
VECT_ERASE, respectively.
diff --git a/lens_default.c b/lens_default.c
index 9f60dae..26ba22e 100644
--- a/lens_default.c
+++ b/lens_default.c
@@ -321,7 +321,7 @@
 	value_destroy(&element);
 
 done:
-	vect_popback(&pointers);
+	VECT_POPBACK(&pointers, struct value *, NULL, NULL);
 	return o;
 }
 
diff --git a/vect.c b/vect.c
index 3a29118..a97977c 100644
--- a/vect.c
+++ b/vect.c
@@ -129,9 +129,30 @@
 }
 
 void
-vect_popback(struct vect *vec)
+vect_erase(struct vect *vec, size_t start, size_t end,
+	   void (*dtor)(void *emt, void *data), void *data)
 {
-	vec->size--;
+	assert(start < vect_size(vec) || start == 0);
+	assert(end <= vect_size(vec));
+
+	/* First, destroy the elements that are to be erased.  */
+	if (dtor != NULL) {
+		size_t i;
+		for (i = start; i < end; ++i)
+			dtor(slot(vec, i), data);
+	}
+
+	/* Now move the tail forward and adjust size.  */
+	memmove(slot(vec, start), slot(vec, end), vec->size - end);
+	vec->size -= end - start;
+}
+
+void
+vect_popback(struct vect *vec,
+	     void (*dtor)(void *emt, void *data), void *data)
+{
+	assert(vect_size(vec) > 0);
+	vect_erase(vec, vect_size(vec)-1, vect_size(vec), dtor, data);
 }
 
 void
@@ -140,12 +161,8 @@
 	if (vec == NULL)
 		return;
 
-	if (dtor != NULL) {
-		size_t i;
-		size_t sz = vect_size(vec);
-		for (i = 0; i < sz; ++i)
-			dtor(slot(vec, i), data);
-	}
+	vect_erase(vec, 0, vect_size(vec), dtor, data);
+	assert(vect_size(vec) == 0);
 	free(vec->data);
 }
 
diff --git a/vect.h b/vect.h
index 0f9951d..1c444c1 100644
--- a/vect.h
+++ b/vect.h
@@ -96,8 +96,32 @@
  * operation was successful, or negative value on error.  */
 int vect_pushback(struct vect *vec, void *eltp);
 
-/* Drop last element of VECP.  */
-void vect_popback(struct vect *vec);
+/* Drop last element of VECP.  This is like calling
+ * vect_erase(VEC, vect_size(VEC)-1, vect_size(VEC), DTOR, DATA);  */
+void vect_popback(struct vect *vec,
+		  void (*dtor)(void *emt, void *data), void *data);
+
+#define VECT_POPBACK(VECP, ELT_TYPE, DTOR, DATA)			\
+	do								\
+		VECT_ERASE((VECP), ELT_TYPE,				\
+			   vect_size(VECP) - 1, vect_size(VECP),	\
+			   DTOR, DATA);					\
+	while (0)
+
+/* Drop elements START (inclusive) to END (non-inclusive) of VECP.  If
+ * DTOR is non-NULL, it is called on each of the removed elements.
+ * DATA is passed verbatim to DTOR.  */
+void vect_erase(struct vect *vec, size_t start, size_t end,
+		void (*dtor)(void *emt, void *data), void *data);
+
+#define VECT_ERASE(VECP, ELT_TYPE, START, END, DTOR, DATA)		\
+	do {								\
+		assert((VECP)->elt_size == sizeof(ELT_TYPE));		\
+		/* Check that DTOR is typed properly.  */		\
+		void (*_dtor_callback)(ELT_TYPE *, void *) = DTOR;	\
+		vect_erase((VECP), (START), (END),			\
+			   (void (*)(void *, void *))_dtor_callback, DATA); \
+	} while (0)
 
 /* Copy element referenced by ELTP to the end of VEC.  See
  * vect_pushback for details.  In addition, make a check whether VECP